├── config ├── __init__.py ├── .gitignore ├── config.py.default └── urls.py ├── models ├── __init__.py ├── notify_type_model.py ├── money_type_model.py ├── money_option_model.py ├── post_thanks_model.py ├── comment_thanks_model.py ├── user_meta_model.py ├── comment_model.py ├── site_model.py ├── cat_model.py ├── money_model.py ├── notify_model.py ├── node_model.py ├── model.py ├── post_model.py └── user_model.py ├── controllers ├── __init__.py ├── index.py ├── notifications.py ├── node.py ├── comment.py ├── post.py ├── admin.py └── user.py ├── libraries ├── __init__.py ├── error.py ├── crumb.py ├── db.py ├── pagination.py ├── widget.py └── helper.py ├── run ├── static ├── .gitignore ├── img │ ├── rss.png │ ├── top.png │ ├── app48.png │ ├── arrow.png │ ├── gold.png │ ├── qbar.png │ ├── reply.png │ ├── bg_item.png │ ├── bronze.png │ ├── favicon.ico │ ├── github.png │ ├── linode.png │ ├── location.png │ ├── logo@2x.png │ ├── mobileme.png │ ├── promoted.png │ ├── shadow.png │ ├── silver.png │ ├── twitter.png │ ├── write48.png │ ├── bg_blended.png │ ├── dot_orange.png │ └── logo_20110809.png ├── icons │ ├── big │ │ └── default.jpg │ ├── tiny │ │ └── default.jpg │ └── normal │ │ └── default.jpg ├── avatar │ ├── big │ │ ├── default.jpg │ │ └── default_f.jpg │ ├── tiny │ │ ├── default.jpg │ │ └── default_f.jpg │ └── normal │ │ ├── default.jpg │ │ └── default_f.jpg ├── css │ ├── desktop.css │ └── main.css └── js │ └── main.js ├── tpl ├── not_found.html ├── user_nf.html ├── login.html ├── no_money.html ├── post_nf.html ├── signup.html ├── admin │ ├── site.html │ ├── create_cat.html │ ├── create_node.html │ ├── node_nf.html │ ├── cat_nf.html │ ├── node_view.html │ ├── set_node_icon.html │ ├── cat_view.html │ ├── index.html │ └── layout.html ├── password.html ├── widget │ ├── ga.html │ ├── site_statics.html │ ├── hot_posts_tody.html │ └── user_panel.html ├── create_post.html ├── about.html ├── node_nf.html ├── avatar.html ├── user_comments.html ├── settings.html ├── user_posts.html ├── recent.html ├── following_posts.html ├── node_favs.html ├── post_favs.html ├── notify.html ├── index.html ├── node_posts.html ├── layout.html ├── profile.html ├── post_view.html └── money_record.html ├── .gitignore ├── README.md ├── code.py ├── index.py.default └── post_bar_init.sql /config/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /models/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /controllers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /libraries/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/.gitignore: -------------------------------------------------------------------------------- 1 | config.py -------------------------------------------------------------------------------- /run: -------------------------------------------------------------------------------- 1 | python code.py 8888 2 | -------------------------------------------------------------------------------- /static/.gitignore: -------------------------------------------------------------------------------- 1 | avatar 2 | icons -------------------------------------------------------------------------------- /static/img/rss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/rss.png -------------------------------------------------------------------------------- /static/img/top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/top.png -------------------------------------------------------------------------------- /static/img/app48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/app48.png -------------------------------------------------------------------------------- /static/img/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/arrow.png -------------------------------------------------------------------------------- /static/img/gold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/gold.png -------------------------------------------------------------------------------- /static/img/qbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/qbar.png -------------------------------------------------------------------------------- /static/img/reply.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/reply.png -------------------------------------------------------------------------------- /static/img/bg_item.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/bg_item.png -------------------------------------------------------------------------------- /static/img/bronze.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/bronze.png -------------------------------------------------------------------------------- /static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/favicon.ico -------------------------------------------------------------------------------- /static/img/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/github.png -------------------------------------------------------------------------------- /static/img/linode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/linode.png -------------------------------------------------------------------------------- /static/img/location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/location.png -------------------------------------------------------------------------------- /static/img/logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/logo@2x.png -------------------------------------------------------------------------------- /static/img/mobileme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/mobileme.png -------------------------------------------------------------------------------- /static/img/promoted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/promoted.png -------------------------------------------------------------------------------- /static/img/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/shadow.png -------------------------------------------------------------------------------- /static/img/silver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/silver.png -------------------------------------------------------------------------------- /static/img/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/twitter.png -------------------------------------------------------------------------------- /static/img/write48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/write48.png -------------------------------------------------------------------------------- /static/img/bg_blended.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/bg_blended.png -------------------------------------------------------------------------------- /static/img/dot_orange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/dot_orange.png -------------------------------------------------------------------------------- /static/icons/big/default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/icons/big/default.jpg -------------------------------------------------------------------------------- /static/img/logo_20110809.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/img/logo_20110809.png -------------------------------------------------------------------------------- /static/avatar/big/default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/avatar/big/default.jpg -------------------------------------------------------------------------------- /static/avatar/tiny/default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/avatar/tiny/default.jpg -------------------------------------------------------------------------------- /static/icons/tiny/default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/icons/tiny/default.jpg -------------------------------------------------------------------------------- /static/avatar/big/default_f.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/avatar/big/default_f.jpg -------------------------------------------------------------------------------- /static/avatar/normal/default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/avatar/normal/default.jpg -------------------------------------------------------------------------------- /static/avatar/tiny/default_f.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/avatar/tiny/default_f.jpg -------------------------------------------------------------------------------- /static/icons/normal/default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/icons/normal/default.jpg -------------------------------------------------------------------------------- /static/avatar/normal/default_f.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HugoPresents/post_bar/HEAD/static/avatar/normal/default_f.jpg -------------------------------------------------------------------------------- /tpl/not_found.html: -------------------------------------------------------------------------------- 1 | $def with (title, text) 2 | $var title: $title 3 | $var widgets=['user_panel_widget'] 4 |
5 | $:text 6 |
-------------------------------------------------------------------------------- /static/css/desktop.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | font-size: 24px; 3 | font-weight: 500; 4 | line-height: 150%; 5 | margin: 0px 0px 10px 0px; 6 | padding: 0px; 7 | } -------------------------------------------------------------------------------- /static/css/main.css: -------------------------------------------------------------------------------- 1 | form label{ 2 | display: block; 3 | padding: 10px 0 10px 0; 4 | font-size: 15px; 5 | text-align: left; 6 | font-weight: bold; 7 | } -------------------------------------------------------------------------------- /libraries/error.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | 3 | # 自定义错误类 4 | 5 | # 自定义值已经存在错误 6 | class ValueExistsError(Exception): 7 | def __init__(self, message): 8 | Exception.__init__(self) 9 | self.message = message -------------------------------------------------------------------------------- /models/notify_type_model.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | __metaclass__ = type 3 | 4 | from models.model import * 5 | class notify_type_model(model): 6 | 7 | def __init__(self): 8 | super(notify_type_model, self).__init__('notify_type') -------------------------------------------------------------------------------- /models/money_type_model.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | __metaclass__ = type 3 | from models.model import * 4 | 5 | class money_type_model(model): 6 | 7 | def __init__(self): 8 | super(money_type_model, self).__init__('money_type') -------------------------------------------------------------------------------- /models/money_option_model.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | __metaclass__ = type 3 | from models.model import * 4 | 5 | class money_option_model(model): 6 | 7 | def __init__(self): 8 | super(money_option_model, self).__init__('money_option') -------------------------------------------------------------------------------- /models/post_thanks_model.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | __metaclass__ = type 3 | import web 4 | from models.model import * 5 | from config.config import * 6 | 7 | class post_thanks_model(model): 8 | 9 | def __init__(self): 10 | super(post_thanks_model, self).__init__('post_thanks') -------------------------------------------------------------------------------- /models/comment_thanks_model.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | __metaclass__ = type 3 | import web 4 | from models.model import * 5 | from config.config import * 6 | 7 | class comment_thanks_model(model): 8 | 9 | def __init__(self): 10 | super(comment_thanks_model, self).__init__('comment_thanks') -------------------------------------------------------------------------------- /tpl/user_nf.html: -------------------------------------------------------------------------------- 1 | $def with (title, crumb) 2 | $var title: $title 3 | $var widgets=None 4 |
5 |
6 |
7 |
$:crumb
8 |
Member Not Found
9 | 10 |
11 |
-------------------------------------------------------------------------------- /tpl/login.html: -------------------------------------------------------------------------------- 1 | $def with (form, title, crumb) 2 | $var title : $title 3 | $var widgets=['user_panel_widget'] 4 |
5 |
6 |
7 |
8 |
9 | $:crumb 10 |
11 |
12 |
13 | $:form.render() 14 |
15 |
16 |
17 |
-------------------------------------------------------------------------------- /tpl/no_money.html: -------------------------------------------------------------------------------- 1 | $def with (title, message, crumb) 2 | $var title: $title 3 | $var widgets=['user_panel_widget'] 4 |
5 |
6 |
7 |
8 |
$:crumb
9 |
$message
10 | 11 |
12 |
-------------------------------------------------------------------------------- /tpl/post_nf.html: -------------------------------------------------------------------------------- 1 | $def with (title, crumb) 2 | $var title: $title 3 | $var widgets=['user_panel_widget'] 4 |
5 |
6 |
7 |
8 |
$:crumb
9 |
Topic Not Found
10 | 11 |
12 |
-------------------------------------------------------------------------------- /tpl/signup.html: -------------------------------------------------------------------------------- 1 | $def with (form, title, crumb) 2 | $var title : $title 3 | $var widgets=['user_panel_widget'] 4 |
5 |
6 |
7 |
8 | $:crumb 9 |
10 |
11 |
12 | $:form.render_css() 13 |
14 |
15 |
16 |
-------------------------------------------------------------------------------- /tpl/admin/site.html: -------------------------------------------------------------------------------- 1 | $def with (title, crumb, form) 2 | $var title:$title 3 | $var widgets=None 4 |
5 |
6 |
7 |
8 | $:crumb 9 |
10 |
11 |
12 | $:form.render_css() 13 |
14 |
15 |
-------------------------------------------------------------------------------- /tpl/password.html: -------------------------------------------------------------------------------- 1 | $def with(title, crumb, form) 2 | $var title:$title 3 | $var widgets=['user_panel_widget'] 4 |
5 |
6 |
7 |
8 | $:crumb 9 |
10 |
11 |
12 | $:form.render() 13 |
14 |
15 |
16 |
-------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | 3 | # Packages 4 | *.egg 5 | *.egg-info 6 | dist 7 | build 8 | eggs 9 | parts 10 | bin 11 | var 12 | sdist 13 | develop-eggs 14 | .installed.cfg 15 | 16 | # Installer logs 17 | pip-log.txt 18 | 19 | # Unit test / coverage reports 20 | .coverage 21 | .tox 22 | 23 | #Translations 24 | *.mo 25 | 26 | #Mr Developer 27 | .mr.developer.cfg 28 | .project 29 | .pydevproject 30 | .settings 31 | sessions 32 | index.py 33 | .DS_store -------------------------------------------------------------------------------- /models/user_meta_model.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | __metaclass__ = type 3 | from models.model import * 4 | from config.config import * 5 | 6 | class user_meta_model(model): 7 | 8 | def __init__(self): 9 | super(user_meta_model, self).__init__('user_meta') 10 | 11 | def count_meta(self, condition): 12 | if condition is not None: 13 | return len(self.get_all(condition)) 14 | else: 15 | return 0 -------------------------------------------------------------------------------- /tpl/widget/ga.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tpl/admin/create_cat.html: -------------------------------------------------------------------------------- 1 | $def with(title, crumb, form) 2 | $var title:$title 3 | $var widgets=None 4 |
5 |
6 |
7 |
8 | $:crumb 9 |
10 |
11 |
12 | $:form.render_css() 13 |
14 |
15 |
16 |
17 |
18 |
-------------------------------------------------------------------------------- /tpl/admin/create_node.html: -------------------------------------------------------------------------------- 1 | $def with(title, crumb, cat, form) 2 | $var title:$title 3 | $var widgets=None 4 |
5 |
6 |
7 |
8 | $:crumb 9 |
10 |
11 |
12 | $:form.render_css() 13 |
14 |
15 |
16 |
17 |
18 | -------------------------------------------------------------------------------- /tpl/create_post.html: -------------------------------------------------------------------------------- 1 | $def with (form, title, crumb) 2 | $var title: $title 3 | $var widgets=['user_panel_widget'] 4 |
5 |
6 |
7 |
$:crumb
8 | 9 |
10 |
11 | $:form.render() 12 |
13 |
14 |
15 |

主题创建指南

16 |

我部署这个网站只想跟大家交流 Python 编程,我没有什么额外目的,希望你也如此,感谢伟大的 V2EX,谢谢。

17 |
18 |
19 |
-------------------------------------------------------------------------------- /tpl/about.html: -------------------------------------------------------------------------------- 1 | $def with(title, crumb) 2 | $var title:$title 3 | $var widgets=['user_panel_widget'] 4 |
5 |
6 |
7 |
8 | $:crumb 9 |
10 |
11 |
12 |

ABOUT

13 |
14 |

这是个抄袭 V2EX 的 demo 站,真相在此

15 |
16 |
17 |
18 |
19 |
-------------------------------------------------------------------------------- /tpl/admin/node_nf.html: -------------------------------------------------------------------------------- 1 | $def with (title, crumb) 2 | $var title:title 3 | $var widgets = None 4 |
5 |
6 |
7 |
8 |
$:crumb
9 |
10 | 你试图编辑的节点不存在,有一种可能: 11 |
12 |
13 |
    14 |
  • 你输入了一个确实不存在的节点名
  • 15 |
16 |
17 |
18 |   返回 后台 19 | 20 |
21 | $if session.user_id: 22 |   返回 我的个人主页 23 |
24 |
25 |
26 |
27 | -------------------------------------------------------------------------------- /config/config.py.default: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | import os 3 | import web 4 | 5 | db = web.database(dbn='mysql', db='post_bar', user='root', pw='rabbit') 6 | render = web.template.render(os.path.abspath(os.path.dirname(__file__)) + '/../tpl/', base='layout', cache = False) 7 | admin_render = web.template.render(os.path.abspath(os.path.dirname(__file__)) + '/../tpl/admin', base='layout') 8 | 9 | web.config.debug = True 10 | 11 | # 模板全局变量 12 | site_title = 'Post_bar' 13 | 14 | # 表单验证规则 15 | notnull = web.form.Validator("必填", bool) 16 | vname = web.form.regexp(r".{3,20}$", ' 用户名长度必须在3-20个字符之间') 17 | vpass = web.form.regexp(r".{5,20}$", ' 密码长度必须在5-20个字符之间') 18 | vemail = web.form.regexp('^\+?[a-z0-9](([-+.]|[_]+)?[a-z0-9]+)*@([a-z0-9]+(\.|\-))+[a-z]{2,6}$', " 请输入正确格式的电子邮箱") -------------------------------------------------------------------------------- /tpl/admin/cat_nf.html: -------------------------------------------------------------------------------- 1 | $def with (title, crumb) 2 | $var title:title 3 | $var widgets = None 4 |
5 |
6 |
7 |
8 |
$:crumb
9 |
10 | 你试图编辑的分类不存在,有几种可能: 11 |
12 |
13 |
    14 |
  • 你输入了一个确实不存在的分类名
  • 15 |
16 |
17 |
18 |   返回 后台 19 | 20 |
21 | $if session.user_id: 22 |   返回 我的个人主页 23 |
24 |
25 |
26 |
27 | -------------------------------------------------------------------------------- /tpl/node_nf.html: -------------------------------------------------------------------------------- 1 | $def with (title, crumb) 2 | $var title:title 3 | $var widgets=['user_panel_widget'] 4 |
5 |
6 |
7 |
8 |
$:crumb
9 |
10 | 你试图查看的节点不存在,有几种可能: 11 |
12 |
13 |
    14 |
  • 你输入了一个确实不存在的节点 ID
  • 15 |
  • 该节点目前位于一个 invisible 位面
  • 16 |
17 |
18 |
19 |   返回 主页 20 | 21 |
22 | $if session.user_id: 23 |   返回 我的个人主页 24 |
25 |
26 |
27 |
-------------------------------------------------------------------------------- /tpl/widget/site_statics.html: -------------------------------------------------------------------------------- 1 | $def with(site_count) 2 |
3 |
4 | 社区运行状态 5 |
6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
注册会员总数$site_count['user']
主题总数$site_count['post']
回复总数$site_count['comment']
21 |
22 |
-------------------------------------------------------------------------------- /libraries/crumb.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | 3 | # 面包屑导航类 4 | from models.site_model import * 5 | class Crumb: 6 | 7 | def __init__(self): 8 | self.separator = ' › ' 9 | 10 | self.content = [[site_model().get_option('title'), '/']] 11 | 12 | def set_separator(self, separator): 13 | self.separator = separator 14 | 15 | def append(self, name, url = None): 16 | self.content.append([name, url]) 17 | 18 | def output(self): 19 | str = '' 20 | i = 0 21 | for item in self.content: 22 | i += 1 23 | if item[1] is None: 24 | str += item[0] 25 | else: 26 | str += '' + item[0] + '' 27 | if i < len(self.content): 28 | str += self.separator 29 | self.clear() 30 | return str 31 | 32 | def clear(self): 33 | self.__init__() -------------------------------------------------------------------------------- /models/comment_model.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | __metaclass__ = type 3 | import web 4 | from models.model import * 5 | from models.comment_thanks_model import * 6 | from config.config import * 7 | 8 | class comment_model(model): 9 | 10 | def __init__(self): 11 | super(comment_model, self).__init__('comment') 12 | self.form = web.form.Form( 13 | web.form.Textarea('content', 14 | notnull, 15 | rows=5, 16 | cols=80, 17 | description='', 18 | post='
', 19 | class_='mll' 20 | ), 21 | web.form.Button('回复',class_='super normal button') 22 | ) 23 | 24 | def count_thanks(self, comment_id): 25 | super(comment_model, self).query('update ' + self._tb + ' set thanks = (select count(*) from ' + comment_thanks_model().table_name() + ' where ' + comment_thanks_model().table_name() + '.comment_id = ' + str(comment_id) + ') where id = ' + str(comment_id)) -------------------------------------------------------------------------------- /tpl/widget/hot_posts_tody.html: -------------------------------------------------------------------------------- 1 | $def with(posts) 2 |
3 |
本日热议主题
4 | $if posts: 5 | $for post in posts: 6 |
7 | 8 | 9 | 12 | 13 | 18 | 19 |
10 | 11 | 14 | 15 | $:post['post'].title 16 | 17 |
20 |
21 |
-------------------------------------------------------------------------------- /models/site_model.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | __metaclass__ = type 3 | import web 4 | from models.model import * 5 | from config.config import * 6 | 7 | class site_model(model): 8 | 9 | def __init__(self): 10 | super(site_model, self).__init__('site') 11 | self.form = web.form.Form( 12 | web.form.Textbox('title', notnull, size=45, description="站点标题", class_='sl'), 13 | web.form.Textbox('site_url', notnull, size=45, description="站点url", class_='sl',post='
不需要加http://'), 14 | web.form.Textbox('cookie_expires', notnull, size=45, description="cookie 过期时间(秒)", class_='sl'), 15 | web.form.Textarea('description', notnull, class_='mle tall', description='站点描述'), 16 | web.form.Button('保存', class_='super normal button') 17 | ) 18 | 19 | def get_options(self): 20 | site = {} 21 | for site_option in self.get_all(): 22 | site[site_option.key] = site_option.value 23 | return site 24 | 25 | def get_option(self, key): 26 | return self.get_one({'key':key}).value -------------------------------------------------------------------------------- /models/cat_model.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | __metaclass__ = type 3 | import web 4 | from models.model import * 5 | from config.config import * 6 | 7 | class cat_model(model): 8 | 9 | def __init__(self): 10 | super(cat_model, self).__init__('category') 11 | self.create_form = web.form.Form( 12 | web.form.Textbox('name', notnull, size=45, description="分类名,用于url,english please", class_='sl'), 13 | web.form.Textbox('display_name', notnull, size=45, description="显示名", class_='sl'), 14 | web.form.Textarea('description', notnull, class_='mle tall', description='分类描述'), 15 | web.form.Button('创建', class_='super normal button') 16 | ) 17 | self.modify_form = web.form.Form( 18 | web.form.Textbox('name', size=45, description="cat name", class_='sl', disabled="disabled"), 19 | web.form.Textbox('display_name', notnull, size=45, description="display name", class_='sl'), 20 | web.form.Textarea('description', notnull, class_='mle tall', description='分类描述'), 21 | web.form.Button('修改', class_='super normal button') 22 | ) -------------------------------------------------------------------------------- /libraries/db.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | # 放弃使用该类 3 | from config.config import db 4 | from libraries.helper import * 5 | 6 | # 自定义join查询方法 7 | # join dict {'join_talbe': ['cur_field', 'target_field']} 8 | # return dict [{'table' : data, 'table_joined' : data}, {'table' : data, 'table_joined' : data}] 9 | 10 | def fetch_join(tb = None, condition = None, join = None, limit = 10, offset = 0): 11 | if tb is None: 12 | return None 13 | where = dict2where(condition) 14 | result = db.select(tb, where = where, limit = limit, offset = offset) 15 | if join is None: 16 | return result 17 | # 返回的结果列表 18 | result_joined = [] 19 | for row in result: 20 | # 遍历出的每一行为一个字典,默认添加{主表:主表该行结果} 21 | row_joined = {tb : row} 22 | for join_table, join_fields in join.items(): 23 | condition = {join_fields[1] : getattr(row, join_fields[0])} 24 | where = dict2where(condition) 25 | join_table_result = db.select(join_table, where = where) 26 | # join 查到的结果字典来更新行结果字典 27 | row_joined.update({join_table : join_table_result}) 28 | #最终将包含join行的结果字典追加到返回的结果列表中 29 | result_joined.append(row_joined) 30 | return result_joined -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | post_bar 2 | ==================== 3 | 4 | web.py 的贴吧 5 | 6 | 1. 为了练手 7 | 8 | 2. 初步想仿一个V2EX,原谅我太爱它了 9 | 10 | 3. apache + wsgi + mysql 11 | 12 | 4. [demo](http://post_bar.rabbit52.com "post_bar") 13 | 14 | 部署 15 | --------------------- 16 | 17 | 1. Apache + mod_wsgi + mysql 环境 18 | 19 | 2. cp index.py.default index.py && cp config/config.py.default config/config.py 并修改相应配置 20 | 21 | 3. 导入post_bar_init.sql 至数据库 22 | 23 | 4. Mac 下用系统 apache 和 python 没有部署成功过~。建议在 linux 和 win 下部署。 Mac 下默认的 python 版本是 2.7.2 从官网下载升级到2.7.3之后能控制台运行 24 | 25 | 5. 环境依赖 必须:python 2.7.3、mysql-python、PIL、webpy 0.37、mysql,可选 Apache、mode_wsgi 26 | 27 | 6. 根目录下的 index.py 是wsgi脚本,code.py 是 web.py 自带服务器脚本。 28 | 29 | 7. 用Apache 部署请 chown -R apache:apache ./ && chmod -R 755 ./ 30 | 31 | 8. 在Apache中创建形如下面的虚拟主机 example: 32 |
33 |     <VirtualHost *:80>
34 |         ServerAdmin admin@localhost
35 |         DocumentRoot /var/www/post_bar
36 |         ServerName post_bar.localhost 
37 |         ErrorLog "logs/post_bar.log"
38 |         CustomLog "logs/post_bar.log" combined
39 |         WSGIScriptAlias / /var/www/post_bar/index.py
40 |         Alias /static /var/www/post_bar/static
41 |         AddType text/html .py
42 |         </VirtualHost>
43 |     
44 | 9. 有疑问请 rabbitzhang52#gmail.com 45 | 46 | 10. 有人问到开源协议,我对那个确实没什么研究,本身就是抄袭了V2EX的设计,我只想跟大家交流一下python 的开发经验。你可以用这个做任何事只要不侵犯丝毫V2EX的权益。 47 | -------------------------------------------------------------------------------- /models/money_model.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | __metaclass__ = type 3 | from models.model import * 4 | from models.money_option_model import * 5 | 6 | class money_model(model): 7 | def __init__(self): 8 | super(money_model, self).__init__('money') 9 | # 将财富配置表数据取出 10 | self.ruler = {} 11 | options = money_option_model().get_all() 12 | for option in options: 13 | # 转成浮点进行下面的保留小数运算 14 | self.ruler[option.key] = float(option.value) 15 | 16 | def cal_post(self, content): 17 | if not isinstance(content, unicode): 18 | content = unicode(content) 19 | length = len_ = float(len(content)) 20 | cost = self.ruler['post_cost'] 21 | len_ -= self.ruler['post_length'] 22 | if len_ > 0: 23 | cost += self.ruler['post_cost_add'] * (len_ / 100) 24 | return length, cost 25 | 26 | def cal_comment(self, content): 27 | if not isinstance(content, unicode): 28 | content = unicode(content) 29 | length = len_ = float(len(content)) 30 | cost = self.ruler['comment_cost'] 31 | len_ -= self.ruler['comment_length'] 32 | if len_ > 0: 33 | cost += self.ruler['post_cost_add'] * (len_ / 100) 34 | return length, cost 35 | 36 | def cal_thanks(self): 37 | return self.ruler['thanks_cost'] -------------------------------------------------------------------------------- /code.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | import sys 3 | if sys.getdefaultencoding() != 'utf-8': 4 | reload(sys) 5 | sys.setdefaultencoding('utf-8') 6 | import web 7 | from config.config import * 8 | from config.urls import * 9 | from libraries import helper 10 | from libraries import widget 11 | from models.site_model import * 12 | from models.user_model import * 13 | from models.notify_model import * 14 | 15 | #web.template.Template.globals['render'] = render 16 | #web.template.Template.globals['admin_render'] = admin_render 17 | #web.template.Template.globals['site_title'] = site_title 18 | web.template.Template.globals['helper'] = helper 19 | web.template.Template.globals['widget'] = widget 20 | web.template.Template.globals['site_options'] = site_model().get_options() 21 | 22 | app = web.application(urls, globals()) 23 | 24 | if web.config.get('_session') is None: 25 | session = web.session.Session(app, web.session.DiskStore('sessions'), initializer={'user_id': None}) 26 | web.config._session = session 27 | else: 28 | session = web.config._session 29 | 30 | #user_model.auth_cookie() 31 | app.add_processor(user_model().auth_cookie) 32 | app.add_processor(notify_model().check) 33 | 34 | # 如果这里不 不将 session 赋值给模板全局变量, 模板中将不能得到此变量 35 | web.template.Template.globals['session'] = session 36 | #web.template.Template.globals['site_url'] = 'http://127.0.0.1:8080' 37 | if __name__ == "__main__": 38 | app.run() -------------------------------------------------------------------------------- /tpl/admin/node_view.html: -------------------------------------------------------------------------------- 1 | $def with (title, crumb, node, form) 2 | $var title:$title 3 | $var widgets=None 4 |
5 |
6 |
7 |
8 | $:crumb 9 |
10 |
11 |
12 | $:form.render_css() 13 |
14 |
15 |
16 |
17 |
18 |
图标上传
19 |
20 | 21 | 22 | 23 | 28 | 29 | 30 | 31 | 32 | 33 |
当前图标 24 |   25 |   26 | 27 |
34 |
35 |
36 |
-------------------------------------------------------------------------------- /tpl/admin/set_node_icon.html: -------------------------------------------------------------------------------- 1 | $def with (title, crumb, node, message='') 2 | $var title: $title 3 | $var widgets = None 4 |
5 |
6 |
7 |
8 |
9 | $:crumb 10 |
11 |
12 | 13 | 14 | 15 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 29 | 30 | 31 | 33 | 34 | 35 |
当前图标 16 |   17 |   18 | 19 |
选择一个图片文件
27 | 支持 2MB 以内的 PNG / JPG / GIF 文件$message
32 |
36 |
37 |
-------------------------------------------------------------------------------- /tpl/avatar.html: -------------------------------------------------------------------------------- 1 | $def with (title, user, crumb, message='') 2 | $var title: $title 3 | $var widgets=['user_panel_widget'] 4 |
5 |
6 |
7 |
8 |
9 | $:crumb 10 |
11 |
12 | 13 | 14 | 15 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 29 | 30 | 31 | 33 | 34 | 35 |
当前头像 16 |   17 |   18 | 19 |
选择一个图片文件
27 | 支持 2MB 以内的 PNG / JPG / GIF 文件$message
32 |
36 |
37 |
38 | -------------------------------------------------------------------------------- /libraries/pagination.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | 3 | import math 4 | 5 | class Pagination: 6 | 7 | def __init__(self, base_url, total, limit = 10): 8 | self.base_url = base_url 9 | self.total = int(math.ceil(float(total)/float(limit))) 10 | if self.total == 0: 11 | self.total = 1 12 | self.limit = limit 13 | self.cur_page = 1 14 | 15 | def output(self): 16 | prev = '' 17 | next = '' 18 | page = '' + str(self.cur_page) + '/' + str(self.total) + '' 19 | if self.cur_page > 1: 20 | prev = '' 21 | if self.cur_page < self.total: 22 | next = '' 23 | string = '' 24 | string += '' 25 | string += '' 26 | string += '' 27 | string += '
' + prev + '' + page + '' + next + '
' 28 | return string 29 | 30 | def true_page(self, page): 31 | page = int(page) 32 | # 加上等于避免 total=0 时出错 33 | if page <= 1: 34 | self.cur_page = 1 35 | elif page > self.total: 36 | self.cur_page = self.total 37 | else: 38 | self.cur_page = page 39 | return self.cur_page -------------------------------------------------------------------------------- /tpl/admin/cat_view.html: -------------------------------------------------------------------------------- 1 | $def with(title, crumb, cat, form, nodes) 2 | $var title:$title 3 | $var widgets=None 4 |
5 |
6 |
7 |
8 | $:crumb 9 |
10 |
11 |
12 | $:form.render_css() 13 |
14 |
15 |
16 |
17 |
18 |
分类下的节点
19 |
20 | $if nodes: 21 | $for node in nodes: 22 | 23 | 24 | 27 | 32 | 33 |
25 | 26 | 28 | $node.display_name 29 |
30 | $node.description 31 |
34 |
35 |
36 |
-------------------------------------------------------------------------------- /tpl/user_comments.html: -------------------------------------------------------------------------------- 1 | $def with (title, comments, total, crumb, pagination) 2 | $var title:$title 3 | $var widgets=['user_panel_widget'] 4 |
5 |
6 |
7 |
8 |
9 | $:crumb 10 |
回复总数  $total
11 |
12 | $if comments: 13 | $ comment_len = len(comments) 14 | $ comment_len_mark = 0 15 | $ class_t = 'inner' 16 | $for comment in comments: 17 | $ comment_len_mark += 1 18 | $if comment_len_mark == comment_len: 19 | $ class_t = 'cell' 20 | $ comment_cur = comment['comment'] 21 | $ post = comment['post'] 22 | $ post_user = comment['post_user'] 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
$helper.stamp2during(comment_cur.time)
回复了 $post_user.name 创建的主题 $:post.title
32 |
33 |
34 |
$:comment_cur.content
35 |
36 |
37 | $:pagination 38 |
39 |
40 |
-------------------------------------------------------------------------------- /index.py.default: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | import sys 3 | import os 4 | if sys.getdefaultencoding() != 'utf-8': 5 | reload(sys) 6 | sys.setdefaultencoding('utf-8') 7 | #abspath = os.path.dirname(__file__) 8 | #sys.path.append(abspath) 9 | #os.chdir(abspath) 10 | 11 | sys.path.append('/var/www/post_bar') 12 | os.chdir('/var/www/post_bar') 13 | import web 14 | from config.config import * 15 | from config.urls import * 16 | from libraries import widget 17 | from libraries import helper 18 | from models.site_model import * 19 | from models.user_model import * 20 | from models.notify_model import * 21 | 22 | #web.template.Template.globals['render'] = render 23 | #web.template.Template.globals['site_title'] = site_title 24 | web.template.Template.globals['widget'] = widget 25 | web.template.Template.globals['site_options'] = site_model().get_options() 26 | web.template.Template.globals['helper'] = helper 27 | 28 | app = web.application(urls, globals(), autoreload = True) 29 | 30 | if web.config.get('_session') is None: 31 | #session = web.session.Session(app, web.session.DiskStore('sessions'), initializer={'user_id': None}) 32 | curdir = os.path.dirname(__file__) 33 | session = web.session.Session(app, web.session.DiskStore(os.path.join(curdir,'sessions')), initializer={'user_id': None}) 34 | web.config._session = session 35 | else: 36 | session = web.config._session 37 | 38 | app.add_processor(user_model().auth_cookie) 39 | app.add_processor(notify_model().check) 40 | 41 | # 如果这里不 不将 session 赋值给模板全局变量, 模板中将不能得到此变量 42 | web.template.Template.globals['session'] = session 43 | #web.template.Template.globals['site_url'] = 'http://post_bar.localhost' 44 | application = app.wsgifunc() 45 | #if __name__ == "__main__": 46 | # app.run() 47 | # 我擦,肿么出现合并死循环了! -------------------------------------------------------------------------------- /controllers/index.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | import web 3 | session = web.config._session 4 | from config.config import render 5 | from models.post_model import * 6 | from models.node_model import * 7 | from models.user_model import * 8 | from models.comment_model import * 9 | from models.cat_model import * 10 | from libraries.crumb import Crumb 11 | from libraries.pagination import * 12 | 13 | class index: 14 | def GET(self): 15 | title = '首页' 16 | #sql = 'SELECT post_id FROM comment GROUP BY post_id ORDER BY MAX(time) DESC LIMIT 20' 17 | #post_ids = post_model().query_result(sql) 18 | posts = post_model().trends() 19 | 20 | cats_result = cat_model().get_all() 21 | cats = [] 22 | for cat_result in cats_result: 23 | cat = {'cat':cat_result} 24 | node = node_model().get_all({'category_id':cat_result.id}) 25 | cat['node'] = node 26 | cats.append(cat) 27 | return render.index(cats, posts, title) 28 | 29 | class recent: 30 | def GET(self): 31 | #sql = 'SELECT post_id FROM comment GROUP BY post_id ORDER BY MAX(time) DESC LIMIT 20' 32 | #post_ids = post_model().query_result(sql) 33 | crumb = Crumb() 34 | limit = 50 35 | total = post_model().count_table() 36 | pagination = Pagination('/recent', total, limit = limit) 37 | page = pagination.true_page(web.input(p=1)['p']) 38 | posts = post_model().trends(limit, (page-1) * limit) 39 | crumb.append('最近的主题') 40 | return render.recent('最近的主题', total, crumb.output(), posts, pagination.output()) 41 | 42 | class about: 43 | def GET(self): 44 | crumb = Crumb() 45 | crumb.append('关于') 46 | return render.about('关于', crumb.output()) 47 | -------------------------------------------------------------------------------- /tpl/settings.html: -------------------------------------------------------------------------------- 1 | $def with (title, user, settings_form, pass_form, crumb) 2 | $var title : $title 3 | $var widgets=['user_panel_widget'] 4 |
5 |
6 |
7 |
8 | $:crumb 9 |
10 | 11 |
12 |
13 | $:settings_form.render() 14 |
15 |
16 |
17 |
18 |
19 |
头像上传
20 |
21 | 22 | 23 | 24 | 29 | 30 | 31 | 32 | 33 | 34 |
当前头像 25 |   26 |   27 | 28 |
35 |
36 |
37 |
38 |
39 |
如果你不打算更改密码,请留空以下区域
更改密码
40 |
41 |
42 | $:pass_form.render() 43 |
44 |
45 |
46 |
-------------------------------------------------------------------------------- /libraries/widget.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | __metaclass__ = type 3 | 4 | import os 5 | import web 6 | import time 7 | import datetime 8 | from models.node_model import * 9 | from models.cat_model import * 10 | from models.post_model import * 11 | from models.user_model import * 12 | from models.comment_model import * 13 | render = web.template.render(os.path.abspath(os.path.dirname(__file__)) + '/../tpl/widget/') 14 | 15 | def generator(widget_name): 16 | return eval(widget_name+'().run()') 17 | 18 | class widget: 19 | 20 | def run(self): 21 | pass 22 | 23 | # 站点统计小组件 24 | class site_statics_widget(widget): 25 | def run(self): 26 | site_count = {} 27 | site_count['user'] = user_model().count_table() 28 | site_count['post'] = post_model().count_table() 29 | site_count['comment'] = comment_model().count_table() 30 | return render.site_statics(site_count) 31 | 32 | # 今日热议主题小组件 33 | class hot_posts_today_widget(widget): 34 | def run(self): 35 | ltime = time.localtime(time.time()) 36 | time_start = int(time.mktime(datetime.datetime(ltime.tm_year, ltime.tm_mon, ltime.tm_mday).timetuple())) 37 | time_end = time_start + 24 * 60 * 60 38 | sql = 'SELECT `post_id` FROM comment WHERE `time` >= '+str(time_start)+' AND `time` <= '+str(time_end)+' GROUP BY post_id ORDER BY count(post_id) DESC LIMIT 10' 39 | post_ids = comment_model().query_result(sql) 40 | posts = [] 41 | for row in post_ids: 42 | post = post_model().get_one({'id':row.post_id}) 43 | user = user_model().get_one({'id':post.user_id}) 44 | posts.append({'post':post, 'user':user}) 45 | return render.hot_posts_tody(posts) 46 | 47 | class user_panel_widget(widget): 48 | def run(self): 49 | return render.user_panel() 50 | 51 | class ga_widget(widget): 52 | def run(self): 53 | return render.ga() -------------------------------------------------------------------------------- /tpl/user_posts.html: -------------------------------------------------------------------------------- 1 | $def with (title, user, posts, total_rows, crumb, pagination) 2 | $var title:$title 3 | $var widgets=['user_panel_widget'] 4 |
5 |
6 |
7 |
8 |
9 | $:crumb 10 |
主题总数  $total_rows
11 | $if posts: 12 | $for post_cur in posts: 13 | $ post = post_cur['post'] 14 | $ comment_user = post_cur['comment_user'] 15 | $ node = post_cur['node'] 16 |
17 | 18 | 19 | 20 | 28 | $if post.comments > 0: 29 | 32 | 33 | 34 |
$:post.title 21 |
22 |
23 | $node.display_name  •  $user.name •  $helper.stamp2during(post.last_update) 24 | $if comment_user is not None: 25 |  •  最后回复来自 $comment_user.name 26 | 27 |
30 | $post.comments 31 |
35 |
36 |
37 | $:pagination 38 |
39 | $else: 40 |
41 | 暂时还莫有~ 42 |
43 |
44 |
-------------------------------------------------------------------------------- /models/notify_model.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | __metaclass__ = type 3 | import web 4 | import re 5 | from models.model import * 6 | from models.user_model import * 7 | from models.notify_type_model import * 8 | from libraries.helper import * 9 | class notify_model(model): 10 | 11 | ruler = re.compile('@[a-z0-9]+') 12 | 13 | def __init__(self): 14 | super(notify_model, self).__init__('notify') 15 | self.types = {} 16 | types = notify_type_model().get_all() 17 | for type_ in types: 18 | self.types[type_.name] = type_.id 19 | 20 | def convert_content(self, content): 21 | user_list = unique_list(self.ruler.findall(content)) 22 | user_list_ = [] 23 | for user_name in user_list: 24 | content = content.replace(user_name, '@'+user_name[1:]+'') 25 | user_list_.append(user_name[1:]) 26 | return content, user_list_ 27 | 28 | def insert_notify(self, user_id, user_list, notify_type, foreign_id): 29 | if isinstance(user_list, list): 30 | for receiver in user_list: 31 | user = user_model().get_one({'name':receiver}) 32 | if user is not None: 33 | self.insert({'user_id':user_id, 'receiver':user.id, 'type_id':self.types[notify_type], 'foreign_id':foreign_id}) 34 | else: 35 | user = user_model().get_one({'name':user_list}) 36 | if user is not None: 37 | self.insert({'user_id':user_id, 'receiver':user.id, 'type_id':self.types[notify_type], 'foreign_id':foreign_id}) 38 | 39 | def mark_as_read(self, receiver): 40 | return self.update({'receiver':receiver}, {'unread':0}) 41 | 42 | def check(self, handler): 43 | if web.config._session.user_id is None: 44 | web.config._session.notifications = 0 45 | else: 46 | web.config._session.notifications = self.count_table({'receiver':web.config._session.user_id, 'unread':'1'}) 47 | return handler() 48 | -------------------------------------------------------------------------------- /tpl/recent.html: -------------------------------------------------------------------------------- 1 | $def with (title, total, crumb, posts, pagination) 2 | $var title : $title 3 | $var widgets=['user_panel_widget'] 4 |
5 |
6 |
7 |
8 |
9 |
共 $total 个主题
10 | $:crumb 11 |
12 | $for post in posts: 13 |
14 | 15 | 16 | 17 | 21 | 23 | 31 | $if post.comments > 0: 32 | 35 | 36 | 37 |
18 | $post.post_user_name 19 | 20 | 22 | $:post.title 24 |
25 |
26 | $post.node_display_name  •  $post.post_user_name •  $helper.stamp2during(post.last_update) 27 | $if post.comment_user_name: 28 |  •  最后回复来自 $post.comment_user_name 29 | 30 |
33 | $post.comments 34 |
38 |
39 |
40 | $:pagination 41 |
42 |
43 |
44 |
45 |
-------------------------------------------------------------------------------- /static/js/main.js: -------------------------------------------------------------------------------- 1 | function check_notify() { 2 | $.post( 3 | '/notifications/check', { 4 | ajax:1 5 | }, function(data) { 6 | if(data != '0') { 7 | $("#notifications").html(''+data+' 条未读提醒'); 8 | document.title += ' ('+data+')'; 9 | } 10 | } 11 | ); 12 | } 13 | 14 | function click_reply(name) { 15 | textarea = $('#content'); 16 | var content = textarea.val(); 17 | if(content.length > 0) { 18 | content += '\n@'+name+' '; 19 | } else { 20 | content += '@'+name+' '; 21 | } 22 | textarea.val(content); 23 | moveEnd(textarea[0]); 24 | //textarea.focus(); 25 | } 26 | 27 | function thankReply(comment_id) { 28 | $.post( 29 | '/comment/thanks', { 30 | comment_id:comment_id 31 | }, function(data) { 32 | if(data.success == 1) { 33 | $("#thank_area_"+comment_id).addClass('thanked').html('感谢已发送') 34 | } else { 35 | alert(data.msg) 36 | } 37 | eval(data.script) 38 | }, 'json' 39 | ) 40 | } 41 | 42 | function thankTopic(post_id) { 43 | $.post( 44 | '/post/thanks', { 45 | post_id:post_id 46 | }, function(data) { 47 | if(data.success == 1) { 48 | $("#topic_thank").html('感谢已发送') 49 | } else { 50 | alert(data.msg) 51 | } 52 | eval(data.script) 53 | }, 'json' 54 | ) 55 | } 56 | 57 | function moveEnd(obj){ 58 | obj.focus(); 59 | var len = obj.value.length; 60 | if (document.selection) { 61 | var sel = obj.createTextRange(); 62 | sel.moveStart('character',len); 63 | sel.collapse(); 64 | sel.select(); 65 | } else if (typeof obj.selectionStart == 'number' && typeof obj.selectionEnd == 'number') { 66 | obj.selectionStart = obj.selectionEnd = len; 67 | } 68 | } -------------------------------------------------------------------------------- /models/node_model.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | __metaclass__ = type 3 | from models.model import * 4 | from config.config import * 5 | 6 | class node_model(model): 7 | 8 | def __init__(self): 9 | super(node_model, self).__init__('node') 10 | self.create_form = web.form.Form( 11 | web.form.Textbox('name', notnull, size=45, description="节点名,用于url,english please", class_='sl'), 12 | web.form.Textbox('display_name', notnull, size=45, description="显示名", class_='sl'), 13 | web.form.Textarea('description', notnull, class_='mle tall', description='分类描述'), 14 | web.form.Button('创建', class_='super normal button') 15 | ) 16 | self.modify_form = web.form.Form( 17 | web.form.Textbox('name', size=45, description="node name", class_='sl', disabled="disabled"), 18 | web.form.Textbox('display_name', notnull, size=45, description="display name", class_='sl'), 19 | web.form.Textarea('description', notnull, class_='mle tall', description='分类描述'), 20 | web.form.Button('修改', class_='super normal button') 21 | ) 22 | 23 | def set_icon(self, filename, node_id): 24 | import Image 25 | import os 26 | path = 'static/icons/' 27 | im = Image.open(path+'tmp/'+filename) 28 | size = im.size 29 | if size[0] > size[1]: 30 | crop_size = size[1] 31 | left = (size[0]-size[1])/2 32 | right = size[1] + left 33 | upper = 0 34 | lower = size[1] 35 | else: 36 | crop_size = size[0] 37 | left = 0 38 | right = size[0] 39 | upper = (size[1]-size[0])/2 40 | lower = size[0] + upper 41 | box = (left, upper, right, lower) 42 | region = im.crop(box) 43 | region.save(path+'tmp/'+filename) 44 | im = Image.open(path+'tmp/'+filename) 45 | im.thumbnail((73, 73), Image.ANTIALIAS) 46 | im.save(path+'big/'+filename) 47 | im.thumbnail((48, 48), Image.ANTIALIAS) 48 | im.save(path+'normal/'+filename) 49 | im.thumbnail((24, 24), Image.ANTIALIAS) 50 | im.save(path+'tiny/'+filename) 51 | del im, region 52 | os.remove(path+'tmp/'+filename) 53 | self.update({'id':node_id}, {'icon':filename}) -------------------------------------------------------------------------------- /tpl/following_posts.html: -------------------------------------------------------------------------------- 1 | $def with (title, posts, total, crumb, pagination) 2 | $var title:$title 3 | $var widgets=['user_panel_widget'] 4 |
5 |
6 |
7 |
8 |
9 | $:crumb 10 |
主题总数  $total 11 |
12 |
13 | $if posts: 14 | $for post_cur in posts: 15 | $ post = post_cur['post'] 16 | $ user = post_cur['user'] 17 | $ node = post_cur['node'] 18 | $ comment_user = post_cur['comment_user'] 19 |
20 | 21 | 22 | 23 | 27 | 29 | 37 | $if post.comments > 0: 38 | 41 | 42 | 43 |
24 | 25 | 26 | 28 | $post.title 30 |
31 |
32 | $node.display_name  •  $user.name •  $helper.stamp2during(post.time) 33 | $if comment_user is not None: 34 |  •  最后回复来自 $comment_user.name 35 | 36 |
39 | $post.comments 40 |
44 |
45 |
46 | $:pagination 47 |
48 |
49 |
-------------------------------------------------------------------------------- /tpl/node_favs.html: -------------------------------------------------------------------------------- 1 | $def with (title, posts, total_rows, crumb, pagination) 2 | $var title:$title 3 | $var widgets=['user_panel_widget'] 4 |
5 |
6 |
7 |
8 |
9 | $:crumb 10 |
主题总数  $total_rows
11 | $if posts: 12 | $for post_cur in posts: 13 | $ user = post_cur['user'] 14 | $ post = post_cur['post'] 15 | $ comment_user = post_cur['comment_user'] 16 | $ node = post_cur['node'] 17 |
18 | 19 | 20 | 21 | 26 | 28 | 38 | $if post.comments > 0: 39 | 42 | 43 |
22 | 23 | 24 | 25 | 27 | $:post.title 29 |
30 | 31 |
32 | $node.display_name  •  33 | $user.name  •  $helper.stamp2during(post.last_update) 34 | $if comment_user is not None: 35 |  •  最后回复来自 $comment_user.name 36 | 37 |
40 | $post.comments 41 |
44 |
45 |
46 | $:pagination 47 |
48 | $else: 49 |
50 | 暂时还莫有~ 51 |
52 |
53 |
-------------------------------------------------------------------------------- /tpl/post_favs.html: -------------------------------------------------------------------------------- 1 | $def with(title, user, posts, crumb, pagination) 2 | $var title:$title 3 | $var widgets=['user_panel_widget'] 4 |
5 |
6 |
7 |
8 |
9 | $:crumb 10 |
主题总数  $user.post_favs
11 | $if posts is not None: 12 | $for post_cur in posts: 13 | $ post = post_cur['post'] 14 | $ user = post_cur['user'] 15 | $ node = post_cur['node'] 16 | $ comment_user = post_cur['comment_user'] 17 |
18 | 19 | 20 | 21 | 25 | 27 | 35 | $if post.comments > 0: 36 | 39 | 40 | 41 |
22 | 23 | 24 | 26 | $:post.title 28 |
29 |
30 | $node.display_name  •  $user.name •  $helper.stamp2during(post.last_update) 31 | $if comment_user is not None: 32 |  •  最后回复来自 $comment_user.name 33 | 34 |
37 | $post.comments 38 |
42 |
43 |
44 | $:pagination 45 |
46 | $else: 47 |
48 | 暂时还莫有~ 49 |
50 |
51 |
-------------------------------------------------------------------------------- /models/model.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | __metaclass__ = type 3 | import web 4 | from config.config import db 5 | from libraries.helper import * 6 | 7 | class model: 8 | _tb = None 9 | def __init__(self, tb = None): 10 | self._tb = tb 11 | self.db = db 12 | 13 | def get_one(self, conditions = None, what = '*', order = None): 14 | where = dict2where(conditions) 15 | try: 16 | return db.select(self._tb, where = where, order = order, limit = 1)[0] 17 | except IndexError: 18 | return None 19 | 20 | def get_all(self, conditions = None, order = None, limit = None, offset = None): 21 | where = dict2where(conditions) 22 | return db.select(self._tb, where = where, order = order, limit = limit, offset = offset) 23 | 24 | def insert(self, values = None): 25 | def q(x): return "(" + x + ")" 26 | if values: 27 | sql_query = 'INSERT INTO '+ self._tb + q(', '.join(values.keys())) + ' VALUES' + q(', '.join('\''+str(_value)+'\'' for _value in values.values())) 28 | db.query(sql_query) 29 | return self.last_insert_id() 30 | else: 31 | return None 32 | 33 | def unique_insert(self, values = None): 34 | if self.get_one(values) is None: 35 | return self.insert(values) 36 | else: 37 | return None 38 | 39 | def update(self, condition, values = None): 40 | where = dict2where(condition) 41 | update = dict2update(values) 42 | if values is not None: 43 | sql_query = 'UPDATE ' + self._tb + ' SET ' + update + ' WHERE ' + where 44 | db.query(sql_query) 45 | return True 46 | 47 | def delete(self, condition, limit = 1): 48 | where = dict2where(condition) 49 | if where is not None: 50 | sql_query = 'DELETE FROM ' + self._tb + ' WHERE ' + where + ' LIMIT 1' 51 | db.query(sql_query) 52 | else: 53 | return None 54 | 55 | def query(self, sql): 56 | db.query(sql) 57 | return True 58 | 59 | def query_result(self, sql): 60 | return db.query(sql) 61 | 62 | def last_insert_id(self): 63 | return db.query('select last_insert_id() as id')[0].id 64 | 65 | def table_name(self): 66 | return self._tb 67 | 68 | def count_table(self, conditions = None): 69 | sql = 'SELECT count(*) as rows FROM ' + self._tb 70 | where = '' 71 | if conditions is not None: 72 | where = dict2where(conditions) 73 | sql += ' WHERE ' + where 74 | return db.query(sql)[0].rows -------------------------------------------------------------------------------- /tpl/admin/index.html: -------------------------------------------------------------------------------- 1 | $def with (title, cats, crumb) 2 | $var title:$title 3 | $var widgets=['site_statics_widget'] 4 |
5 |
6 |
7 |
8 |
9 | $:crumb 10 |
11 |
12 | 13 | 14 | 15 | 20 | 21 |
站点设置 16 |
17 |
18 | 站点名,描述 19 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | 添加新分类
31 |
32 |
分类 33 |
34 |
35 |
36 |
37 | 38 | 39 | $if cats: 40 | $for cat_cur in cats: 41 | $ cat = cat_cur['cat'] 42 | 43 | 46 | 52 | 53 | 54 |
44 | $cat_cur['node_total'] 45 | 47 | $cat.display_name 48 |
49 |
50 |  $cat.description 51 |
55 |
56 |
57 |
-------------------------------------------------------------------------------- /config/urls.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | pre_fix = 'controllers.' 3 | urls = ( 4 | # 首页 5 | '/index', pre_fix + 'index.index', 6 | # 关于 7 | '/about', pre_fix + 'index.about', 8 | # 最近的主题 9 | '/recent', pre_fix + 'index.recent', 10 | # 浏览主题 11 | '/post/(\d+)', pre_fix + 'post.view', 12 | # 创建主题 13 | '/create/([a-z\-]*)', pre_fix + 'post.create', 14 | # 感谢主题 15 | '/post/thanks', pre_fix + 'post.thanks', 16 | # 节点主题列表 17 | '/node/([a-z\-]*)', pre_fix + 'node.index', 18 | # 评论主题 19 | '/comment/(\d+)', pre_fix + 'comment.create', 20 | # 感谢评论 21 | '/comment/thanks', pre_fix + 'comment.thanks', 22 | # 注册 23 | '/signup', pre_fix + 'user.signup', 24 | # 登录 25 | '/login', pre_fix + 'user.login', 26 | # 注销 27 | '/logout', pre_fix + 'user.logout', 28 | # 提醒 29 | '/notifications', pre_fix + 'notifications.index', 30 | # 提醒检查 31 | '/notifications/check', pre_fix + 'notifications.check', 32 | # 删除提醒 33 | '/notifications/delete/(\w+)', pre_fix + 'notifications.delete', 34 | # 设置 35 | '/settings', pre_fix + 'user.settings', 36 | # 上传头像 37 | '/settings/avatar', pre_fix + 'user.avatar', 38 | # 设置密码 39 | '/settings/password', pre_fix + 'user.password', 40 | # 用户中心 41 | '/profile/(\w+)', pre_fix + 'user.profile', 42 | # 财富中心 43 | '/balance', pre_fix + 'user.balance', 44 | # 关注用户 45 | '/follow/(\w+)', pre_fix + 'user.follow', 46 | # 取消关注 47 | '/unfollow/(\w+)', pre_fix + 'user.unfollow', 48 | # 来自关注人的帖子 49 | '/my/following', pre_fix +'user.following', 50 | # 收藏帖子 51 | '/post/fav/(\d+)', pre_fix + 'post.fav', 52 | # 取消收藏帖子 53 | '/post/unfav/(\d+)', pre_fix + 'post.unfav', 54 | # 收藏的主题 55 | '/my/posts', pre_fix + 'user.post_favs', 56 | # 用户创建的主题 57 | '/profile/(\w+)/posts', pre_fix + 'user.posts', 58 | # 用户创建的回复 59 | '/profile/(\w+)/comments', pre_fix + 'user.comments', 60 | # 收藏节点 61 | '/node/fav/([a-z\-]*)', pre_fix + 'node.fav', 62 | # 收藏节点的主题 63 | '/my/nodes', pre_fix + 'user.node_favs', 64 | # 取消收藏节点 65 | '/node/unfav/([a-z\-]*)', pre_fix + 'node.unfav', 66 | # 后台管理 67 | '/admin', pre_fix + 'admin.index', 68 | # 站点设置 69 | '/admin/site', pre_fix + 'admin.site', 70 | # 分类编辑 71 | '/admin/cat/([a-z\-]*)', pre_fix + 'admin.cat', 72 | # 添加分类 73 | '/admin/create_cat', pre_fix + 'admin.create_cat', 74 | # 添加节点 75 | '/admin/create_node/([a-z\-]*)', pre_fix + 'admin.create_node', 76 | # 编辑节点 77 | '/admin/node/([a-z\-]*)', pre_fix + 'admin.node', 78 | # 设置节点图标 79 | '/admin/node/icon/([a-z\-]*)', pre_fix + 'admin.set_node_icon', 80 | # 其他 81 | '/.*', pre_fix + 'index.index' 82 | ) -------------------------------------------------------------------------------- /controllers/notifications.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | import web 3 | session = web.config._session 4 | from config.config import render 5 | from models.post_model import * 6 | from models.node_model import * 7 | from models.user_model import * 8 | from models.user_meta_model import * 9 | from models.comment_model import * 10 | from models.notify_model import * 11 | from models.notify_type_model import * 12 | from libraries.crumb import Crumb 13 | from libraries.pagination import Pagination 14 | 15 | class index: 16 | 17 | def __init__(self): 18 | if session.user_id is None: 19 | raise web.SeeOther('/login?next=/notifications') 20 | def GET(self): 21 | crumb = Crumb() 22 | condition = {'receiver':session.user_id} 23 | total = notify_model().count_table(condition) 24 | limit = 10 25 | pagination = Pagination('/notifications', total, limit = limit) 26 | page = pagination.true_page(web.input(p=1)['p']) 27 | notify_result = notify_model().get_all(condition, order = 'id DESC', limit = limit, offset = (page-1)*limit) 28 | notifications = [] 29 | if notify_result is not None: 30 | for notify in notify_result: 31 | post = None 32 | user = None 33 | comment = None 34 | notify_type = notify_type_model().get_one({'id':notify.type_id}).name 35 | user = user_model().get_one({'id':notify.user_id}) 36 | if notify_type == 'post_at': 37 | post = post_model().get_one({'id':notify.foreign_id}) 38 | elif notify_type == 'comment' or notify_type == 'comment_at': 39 | comment = comment_model().get_one({'id':notify.foreign_id}) 40 | post = post_model().get_one({'id':comment.post_id}) 41 | notifications.append({'notify':notify, 'post':post, 'user':user, 'comment':comment, 'type':notify_type}) 42 | notify_model().mark_as_read(session.user_id) 43 | crumb.append('提醒系统') 44 | return render.notify('提醒系统', crumb.output(), total, notifications, pagination.output()) 45 | 46 | class check: 47 | 48 | def __init__(self): 49 | if session.user_id is None: 50 | return 0 51 | def POST(self): 52 | if web.input(ajax = None): 53 | return notify_model().count_table({'receiver':session.user_id, 'unread':'1'}) 54 | else: 55 | return '0' 56 | 57 | class delete: 58 | 59 | def __init__(self): 60 | if session.user_id is None: 61 | raise web.SeeOther('/login?next=/notifications') 62 | def GET(self, id): 63 | notify = notify_model().get_one({'id':id}) 64 | if notify is not None and notify.receiver == session.user_id: 65 | notify_model().delete({'id':id}) 66 | raise web.SeeOther('/notifications') -------------------------------------------------------------------------------- /tpl/notify.html: -------------------------------------------------------------------------------- 1 | $def with(title, crumb, total, notifications, pagination) 2 | $var title:$title 3 | $var widgets=['user_panel_widget'] 4 |
5 |
6 | 7 |
8 |
9 |
10 | 总共收到提醒  $total 11 |
12 | $:crumb 13 |
14 | $for notify_cur in notifications: 15 | $ notify = notify_cur['notify'] 16 | $ post = notify_cur['post'] 17 | $ comment = notify_cur['comment'] 18 | $ user = notify_cur['user'] 19 | $ type = notify_cur['type'] 20 |
21 | 22 | 23 | 24 | 27 | 47 | 48 | 49 |
25 | 26 | 28 | $if type == 'post_at': 29 | $user.name 在发表 $post.title 时提到了你   $helper.stamp2during(post.time) 删除 30 |
31 |
32 | $:post.content 33 |
34 | $elif type == 'comment': 35 | $user.name$post.title 里回复了你   $helper.stamp2during(comment.time) 删除 36 |
37 |
38 | $:comment.content 39 |
40 | $elif type == 'comment_at': 41 | $user.name 在回复 $post.title 时提到了你   $helper.stamp2during(comment.time) 删除 42 |
43 |
44 | $:comment.content 45 |
46 |
50 |
51 |
52 | $:pagination 53 |
54 |
55 |
56 |
-------------------------------------------------------------------------------- /models/post_model.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | __metaclass__ = type 3 | import web 4 | from models.model import * 5 | from models.comment_model import * 6 | from models.post_thanks_model import * 7 | from config.config import * 8 | 9 | class post_model(model): 10 | 11 | def __init__(self): 12 | super(post_model, self).__init__('post') 13 | self.form = web.form.Form( 14 | web.form.Textarea('title', notnull, class_='mle', description=''), 15 | web.form.Textarea('content', notnull, class_='mle tall', description=''), 16 | web.form.Button('创建', class_='super normal button') 17 | ) 18 | 19 | def add_view(self, post_id): 20 | super(post_model, self).query('UPDATE ' + self._tb + ' SET views = views + 1 WHERE id=' + str(post_id)) 21 | 22 | def count_comment(self, post_id): 23 | super(post_model, self).query('update ' + self._tb + ' set comments = (select count(*) from ' + comment_model().table_name() + ' where ' + comment_model().table_name() + '.post_id = ' + str(post_id) + ') where id = ' + str(post_id)) 24 | 25 | def count_thanks(self, post_id): 26 | super(post_model, self).query('update ' + self._tb + ' set thanks = (select count(*) from ' + post_thanks_model().table_name() + ' where ' + post_thanks_model().table_name() + '.post_id = ' + str(post_id) + ') where id = ' + str(post_id)) 27 | 28 | def trends(self, limit = 20, offset = 0, node_id = None): 29 | select = [ 30 | 'p.id', 31 | 'p.title', 32 | 'p.last_update', 33 | 'p.comments', 34 | 'u.id AS post_user_id', 35 | 'u.name AS post_user_name', 36 | 'u.avatar AS post_user_avatar', 37 | 'c.time', 38 | 'c.content', 39 | 'cu.name AS comment_user_name' 40 | ] 41 | 42 | from_ = [ 43 | 'post p', 44 | 'JOIN user u ON p.user_id = u.id', 45 | 'LEFT JOIN comment c ON c.post_id = p.id', 46 | 'LEFT JOIN comment c1 ON c1.post_id = c.post_id AND c1.`time` > c.`time`' 47 | 'LEFT JOIN user cu ON cu.id = c.user_id' 48 | ] 49 | 50 | where = [ 51 | 'c1.time IS NULL' 52 | ] 53 | 54 | if node_id is not None: 55 | where.append('p.node_id = $node_id') 56 | else: 57 | select.append('n.name AS node_name') 58 | select.append('n.display_name AS node_display_name') 59 | from_.append('JOIN node n ON n.id = p.node_id') 60 | sql = 'SELECT ' 61 | sql += ', '.join(select) 62 | sql += ' FROM ' 63 | sql += '\n'.join(from_) 64 | sql += '\n WHERE ' 65 | sql += ' AND '.join(where) 66 | sql += ''' 67 | GROUP BY p.id 68 | ORDER BY p.last_update DESC 69 | LIMIT $offset, $limit 70 | ''' 71 | return self.db.query(sql, vars = {'limit': limit, 'offset':offset, 'node_id': node_id}) -------------------------------------------------------------------------------- /tpl/index.html: -------------------------------------------------------------------------------- 1 | $def with (cats, posts, title = None) 2 | $var title : $title 3 | $var widgets = ['user_panel_widget', 'hot_posts_today_widget', 'site_statics_widget'] 4 |
5 |
6 |
7 |
8 |
9 | 全部 10 |
11 | $for post in posts: 12 |
13 | 14 | 15 | 16 | 20 | 22 | 30 | $if post.comments > 0: 31 | 34 | 35 | 36 |
17 | $post.post_user_name 18 | 19 | 21 | $:post.title 23 |
24 |
25 | $post.node_display_name  •  $post.post_user_name •  $helper.stamp2during(post.last_update) 26 | $if post.comment_user_name: 27 |  •  最后回复来自 $post.comment_user_name 28 | 29 |
32 | $post.comments 33 |
37 |
38 |
39 | 更多新主题 40 |
41 |
42 |
43 |
44 |
45 |
$site_options['title'] / 节点导航
46 | $for cat_cur in cats: 47 | $ cat = cat_cur['cat'] 48 | $ nodes = cat_cur['node'] 49 |
50 | 51 | 52 | 53 | 56 | 60 | 61 | 62 |
54 | $cat.display_name 55 | 57 | $for node in nodes: 58 | $node.display_name    59 |
63 |
64 |
65 |
-------------------------------------------------------------------------------- /tpl/node_posts.html: -------------------------------------------------------------------------------- 1 | $def with (posts, node, total_rows, node_fav, crumb, pagination) 2 | $var title: $node.description 3 | $var widgets=['user_panel_widget'] 4 |
5 |
6 |
7 |
8 |
9 |
10 | 11 |
12 |
13 | 主题总数 14 | $total_rows 15 |  •  16 | $if node_fav: 17 | 取消收藏这个节点 18 | $else: 19 | 收藏这个节点 20 |
21 | $:crumb 22 |
23 |
24 | $node.description 25 |
26 |
27 | 28 |
29 |
30 | $if posts: 31 | $for post in posts: 32 |
33 | 34 | 35 | 36 | 38 | 40 | 48 | $if post.comments > 0: 49 | 52 | 53 | 54 |
37 | 39 | $:post.title 41 |
42 |
43 | $post.post_user_name  •  $helper.stamp2during(post.last_update) 44 | $if post.comment_user_name: 45 |  •  最后回复来自 $post.comment_user_name 46 | 47 |
50 | $post.comments 51 |
55 |
56 |
57 | $:pagination 58 |
59 | $else: 60 |
61 | 暂时还莫有~ 62 |
63 |
64 |
65 |
-------------------------------------------------------------------------------- /controllers/node.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | import web 3 | session = web.config._session 4 | from config.config import render 5 | from models.post_model import * 6 | from models.node_model import * 7 | from models.user_model import * 8 | from models.user_meta_model import * 9 | from models.comment_model import * 10 | from libraries.crumb import Crumb 11 | from libraries.pagination import Pagination 12 | 13 | # 显示某节点的文章 14 | class index: 15 | 16 | def __init__(self): 17 | self.crumb = Crumb() 18 | 19 | def GET(self, node_name): 20 | limit = 10 21 | node = node_model().get_one({'name': node_name}) 22 | if node is None: 23 | self.crumb.append('节点未找到') 24 | return render.node_nf('节点未找到', self.crumb.output()) 25 | else: 26 | self.crumb.append(node.display_name) 27 | node_fav = False 28 | if session.user_id: 29 | if user_meta_model().get_one({'user_id':session.user_id, 'meta_key':'node_fav', 'meta_value':node.id}): 30 | node_fav = True 31 | total_rows = post_model().count_table({'node_id':node.id}) 32 | pagination = Pagination('/node/'+node_name, total_rows, limit = limit) 33 | page = pagination.true_page(web.input(p=1)['p']) 34 | posts = post_model().trends(limit, (page-1) * limit, node.id) 35 | return render.node_posts(posts, node, total_rows, node_fav, self.crumb.output(), pagination.output()) 36 | 37 | class fav: 38 | 39 | def __init__(self): 40 | self.crumb = Crumb() 41 | 42 | def GET(self, node_name): 43 | node = node_model().get_one({'name': node_name}) 44 | if node is None: 45 | self.crumb.append('节点未找到') 46 | return render.node_nf('节点未找到', self.crumb.output()) 47 | if session.user_id is None: 48 | raise web.SeeOther('/login?next=/node/'+node_name) 49 | user_meta_model().unique_insert({'user_id':session.user_id, 'meta_key':'node_fav', 'meta_value':node.id}) 50 | user_model().update({'id':session.user_id}, {'node_favs':user_meta_model().count_meta({'user_id':session.user_id, 'meta_key':'node_fav'})}) 51 | user_model().update_session(session.user_id) 52 | raise web.SeeOther('/node/'+node_name) 53 | 54 | class unfav: 55 | 56 | def __init__(self): 57 | self.crumb = Crumb() 58 | 59 | def GET(self, node_name): 60 | node = node_model().get_one({'name': node_name}) 61 | if node is None: 62 | self.crumb.append('节点未找到') 63 | return render.node_nf('节点未找到', self.crumb.output()) 64 | if session.user_id is None: 65 | raise web.SeeOther('/login?next=/node/'+node_name) 66 | user_meta_model().delete({'user_id':session.user_id, 'meta_key':'node_fav', 'meta_value':node.id}) 67 | user_model().update({'id':session.user_id}, {'node_favs':user_meta_model().count_meta({'user_id':session.user_id, 'meta_key':'node_fav'})}) 68 | user_model().update_session(session.user_id) 69 | raise web.SeeOther('/node/'+node_name) -------------------------------------------------------------------------------- /tpl/widget/user_panel.html: -------------------------------------------------------------------------------- 1 | $if session.user_id: 2 |
3 |
4 | 5 | 6 | 7 | 12 | 14 | 19 | 20 | 21 |
8 | 9 | 10 | 11 | 13 | $session.name 15 |
16 |
17 | $session.signature 18 |
22 |
23 |
24 | 25 | 26 | 34 | 42 | 50 | 51 |
27 | 28 | $session.node_favs 29 |
30 |
31 | 节点收藏 32 |
33 |
35 | 36 | $session.post_favs 37 |
38 |
39 | 主题收藏 40 |
41 |
43 | 44 | $session.user_favs 45 |
46 |
47 | 特别关注 48 |
49 |
52 |
53 |
54 | 59 | 60 | $if session.notifications > 0: 61 | $session.notifications 条未读提醒 62 | $else: 63 | 0 条未读提醒 64 | 65 | 66 |
67 |
68 | $else: 69 |
70 |
71 | 这里填什么? 72 |
73 |
74 | $site_options['description'] 75 |
76 |
77 |
78 |
79 |
现在注册 80 |
81 |
82 |
83 |
84 | 已注册用户请 登入
85 |
86 |
-------------------------------------------------------------------------------- /tpl/admin/layout.html: -------------------------------------------------------------------------------- 1 | $def with (content, title = 'Post_bar') 2 | $if content.title is not None: 3 | $ title = content.title + ' | ' + site_options['title'] 4 | $else: 5 | $ title = site_options['title'] 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | $title 17 | 18 | 19 |
20 |
21 |
22 | 23 | 24 | 28 | 48 | 60 |
25 | V2EX 26 | 27 | 29 | 40 | 47 | 49 | $if session.user_id: 50 | $session.name    51 | 设置    52 | $if session.user_id == 1: 53 | 后台    54 | 登出 55 | $else: 56 | 首页    57 | 注册    58 | 登录    59 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | $if content.widgets: 68 | $for cur_widget in content.widgets: 69 |
70 |
71 | $:widget.generator(cur_widget) 72 |
73 | $:content 74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | 关于   86 |
87 |
88 | $site_options['description'] 89 |
90 |
91 | Copy from V2EX By Rabbit_52 92 |
93 |
94 |
95 | 96 | -------------------------------------------------------------------------------- /tpl/layout.html: -------------------------------------------------------------------------------- 1 | $def with (content, title = 'Post_bar') 2 | $if content.title is not None: 3 | $ title = content.title + ' | ' + site_options['title'] 4 | $else: 5 | $ title = site_options['title'] 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | $:widget.generator('ga_widget') 17 | $title 18 | 19 | 20 |
21 |
22 |
23 | 24 | 25 | 29 | 49 | 61 |
26 | $site_options['title'] 27 | 28 | 30 | 41 | 48 | 50 | $if session.user_id: 51 | $session.name    52 | 设置    53 | $if session.user_id == 1: 54 | 后台    55 | 登出 56 | $else: 57 | 首页    58 | 注册    59 | 登录    60 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | $if content.widgets: 69 | $for cur_widget in content.widgets: 70 |
71 |
72 | $:widget.generator(cur_widget) 73 |
74 | $:content 75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | 关于   87 |
88 |
89 | $site_options['description'] 90 |
91 |
92 | Copy from V2EX By Rabbit_52 93 |
94 |
95 |
96 | 97 | -------------------------------------------------------------------------------- /libraries/helper.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | # 辅助函数库 3 | 4 | # 将字典转换成sql where字符串 5 | def dict2where(dict): 6 | if dict is not None: 7 | where_str = '' 8 | for key, value in dict.items(): 9 | if isinstance(value, list): 10 | where_str += '`'+str(key)+'`' + ' in (' + ', '.join('\''+str(_value)+'\'' for _value in value) + ') AND' 11 | else: 12 | where_str += '`'+str(key)+'`' + '=\'' + str(value) + '\' AND ' 13 | where_len = len(where_str) 14 | where = where_str[0 : where_len-4] 15 | return where 16 | else: 17 | return None 18 | 19 | # 将字典转换成sql update语句 20 | def dict2update(dict): 21 | if dict is not None: 22 | update_str = '' 23 | for key,value in dict.items(): 24 | update_str += '`'+str(key)+'`' + '=\'' + str(value) + '\',' 25 | update_len = len(update_str) 26 | update = update_str[0:update_len-1] 27 | return update 28 | 29 | # 时间戳转为到现在的时间差 30 | def stamp2during(stamp = 0): 31 | import time 32 | from math import ceil 33 | # 对当前时间向上取整,防止出现负数~ 34 | cur_time = int(ceil(time.time())) 35 | during = cur_time-stamp 36 | if during < 30: 37 | return str(during) + ' 秒前' 38 | if during < 60: 39 | return '半分钟前' 40 | if during < 3600: 41 | return str(int(during/60)) + ' 分钟前' 42 | if during < 24*3600: 43 | return str(int(during/3600)) + ' 小时前' 44 | if during < 365*24*3600: 45 | return str(int(during/(24*3600))) + ' 天前' 46 | #return str(int(during/(24*3600))) + '天前' 47 | ltime=time.localtime(stamp) 48 | return time.strftime("%Y-%m-%d %H:%m:%S %p", ltime) 49 | 50 | # 将时间戳转为合适格式时间 51 | def stamp2time(stamp = 0): 52 | import time 53 | ltime=time.localtime(stamp) 54 | return time.strftime("%Y-%m-%d %H:%m:%S %p", ltime) 55 | 56 | # 时间戳转为时间 57 | def cur_date(format = "%Y-%m-%d %H:%m:%S %p"): 58 | import time 59 | import datetime 60 | return datetime.datetime.now().strftime(format) 61 | 62 | def avatar_url(avatar, mode = 'normal'): 63 | import os 64 | avatar = str(avatar) 65 | path = 'static/avatar/'+mode+'/' 66 | if os.path.exists(path+avatar) and avatar != '': 67 | return '/'+path+avatar 68 | else: 69 | return '/'+path+'default.jpg' 70 | 71 | def icon_url(icon, mode = 'normal'): 72 | import os 73 | icon = str(icon) 74 | path = 'static/icons/'+mode+'/' 75 | if os.path.exists(path+icon) and icon != '': 76 | return '/'+path+icon 77 | else: 78 | return '/'+path+'default.jpg' 79 | 80 | def display_money(money): 81 | money = int(money) 82 | string = '' 83 | gold = 0 84 | silver = 0 85 | bronze = 0 86 | if money >= 10000: 87 | gold = money // 10000 88 | money = money % 10000 89 | string += str(gold) + ' ' 90 | string += 'G' 91 | if money >= 100: 92 | silver = money // 100 93 | money = money % 100 94 | if gold: 95 | string += ' ' 96 | string += str(silver) + ' ' 97 | string += 'S' 98 | bronze = money 99 | if silver: 100 | string += ' ' 101 | if bronze <= 0 and (gold or silver): 102 | pass 103 | else: 104 | string += str(bronze) + ' ' 105 | string += 'B' 106 | return string 107 | 108 | #转成html实体 109 | def str2entity(str): 110 | import htmlentitydefs 111 | str = unicode(str) 112 | to = u'' 113 | for i in str: 114 | if ord(i) in htmlentitydefs.codepoint2name: 115 | name = htmlentitydefs.codepoint2name.get(ord(i)) 116 | to += "&" + name + ";" 117 | else: 118 | to += i 119 | return to 120 | 121 | def html2db(str): 122 | #str = str.encode() 123 | str = str.replace("'", "\\'").replace('"', '\\"').replace('$', '\\$').replace('<', '<').replace('>', '>') 124 | return str 125 | 126 | def unique_list(list_): 127 | u_list = [] 128 | for item in list_: 129 | if item not in u_list: 130 | u_list.append(item) 131 | return u_list 132 | 133 | def list_diff(list_1, list_2): 134 | list_ = [] 135 | for item in list_1: 136 | if item not in list_2: 137 | list_.append(item) 138 | return list_ 139 | 140 | def strip_tags(html): 141 | from HTMLParser import HTMLParser 142 | html=html.strip() 143 | html=html.strip("\n") 144 | result=[] 145 | parse=HTMLParser() 146 | parse.handle_data=result.append 147 | parse.feed(html) 148 | parse.close() 149 | return "".join(result) -------------------------------------------------------------------------------- /controllers/comment.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | import web 3 | session = web.config._session 4 | import time 5 | from config.config import render 6 | from models.comment_model import * 7 | from models.comment_thanks_model import * 8 | from models.post_model import * 9 | from models.money_model import * 10 | from models.money_type_model import * 11 | from models.user_model import * 12 | from models.notify_model import * 13 | from models.notify_type_model import * 14 | from libraries.helper import * 15 | 16 | class create: 17 | 18 | def __init__(self): 19 | self.form = comment_model().form 20 | 21 | def GET(self, post_id): 22 | raise web.SeeOther('/post/' + post_id) 23 | 24 | def POST(self, post_id): 25 | if session.user_id is None: 26 | raise web.SeeOther('/login') 27 | post = post_model().get_one({'id':post_id}) 28 | if post is not None: 29 | if not self.form.validates(): 30 | raise web.SeeOther('/post/' + post_id) 31 | else: 32 | user_model().update_session(session.user_id) 33 | length, cost = money_model().cal_comment(self.form.d.content) 34 | if session.money < cost: 35 | self.crumb.append('财富不够') 36 | return render.no_money('财富不够', '你的财富值不够,不能创建改主题 :(', self.crumb.output()) 37 | content = html2db(self.form.d.content) 38 | content, receiver_list = notify_model().convert_content(content) 39 | create_time = time.time() 40 | comment_id = comment_model().insert({'user_id' : session.user_id, 'post_id' : post_id, 'content' : content, 'time' : create_time}) 41 | money_type_id = money_type_model().get_one({'name':'comment'})['id'] 42 | money_model().insert({'user_id':session.user_id, 'money_type_id':money_type_id, 'amount':-cost, 'length':length, 'balance':user_model().update_money(session.user_id, -cost), 'foreign_id':comment_id}) 43 | if session.user_id != post.user_id: 44 | money_model().insert({'user_id':post.user_id, 'money_type_id':money_type_id, 'amount':cost, 'length':length, 'foreign_id':comment_id, 'balance':user_model().update_money(post.user_id, cost)}) 45 | # notify 46 | notify_model().insert({'user_id':session.user_id, 'receiver':post.user_id, 'type_id':notify_type_model().get_one({'name':'comment'}).id, 'foreign_id':comment_id}) 47 | # notify 48 | receiver_list = list_diff(receiver_list, [session.name, user_model().get_one({'id':post.user_id}).name]) 49 | notify_model().insert_notify(session.user_id, receiver_list, 'comment_at', comment_id) 50 | user_model().update_session(session.user_id) 51 | post_model().update({'id':post_id}, {'last_update':create_time}) 52 | post_model().count_comment(post_id) 53 | raise web.SeeOther('/post/' + post_id) 54 | else: 55 | raise web.SeeOther('/post/' + post_id) 56 | 57 | class thanks: 58 | def POST(self): 59 | import json 60 | json_dict = {'success':0, 'msg':'', 'script':''} 61 | comment_id = web.input(comment_id=None)['comment_id'] 62 | comment = comment_model().get_one({'id':comment_id}) 63 | if comment_id and comment: 64 | if session.user_id is None: 65 | post = post_model().get_one({'id':comment.post_id}) 66 | json_dict['msg'] = '你要先登录的亲' 67 | json_dict['script'] = 'location.href=\'/login?next=/post/'+str(post.id)+'#reply-'+str(comment_id)+'\'' 68 | elif comment.user_id != session.user_id: 69 | comment_thanks_id = comment_thanks_model().unique_insert({'user_id':session.user_id, 'comment_id':comment_id}) 70 | if comment_thanks_id: 71 | comment_thanks_model().update({'id':comment_thanks_id}, {'time':int(time.time())}) 72 | cost = money_model().cal_thanks() 73 | money_type_id = money_type_model().get_one({'name':'comment_thanks'})['id'] 74 | money_model().insert({'user_id':session.user_id, 'money_type_id':money_type_id, 'amount':-cost, 'balance':user_model().update_money(session.user_id, -cost), 'foreign_id':comment_thanks_id}) 75 | money_model().insert({'user_id':comment.user_id, 'money_type_id':money_type_id, 'amount':cost, 'foreign_id':comment_thanks_id, 'balance':user_model().update_money(comment.user_id, cost)}) 76 | comment_model().count_thanks(comment_id) 77 | user_model().update_session(session.user_id) 78 | json_dict['success'] = 1 79 | else: 80 | json_dict['msg'] = '你已经感谢过了不是吗?' 81 | else: 82 | json_dict['msg'] = '你不能感谢你自己不是吗?' 83 | else: 84 | json_dict['message'] = '评论不存在' 85 | return json.dumps(json_dict) -------------------------------------------------------------------------------- /tpl/profile.html: -------------------------------------------------------------------------------- 1 | $def with (title, user, posts, comments, following) 2 | $var title: $title 3 | $var widgets = None 4 |
5 |
6 |
7 |
8 |
9 | 10 | 11 | 14 | 33 | 34 |
12 | 13 | 15 |
16 | $if user.id != session.user_id: 17 | $if following: 18 | 19 | $else: 20 | 21 |
22 |

$user.name

23 | $user.signature 24 |
25 |
26 | $site_options['title'] 第 $user.id 号会员,加入于 $helper.stamp2time(user.regist_time) 27 |
28 |
29 |
30 | $:helper.display_money(user.money) 31 |
32 |
35 |
36 |
37 |
38 |
39 | $if user.outsite_link: 40 |
41 |
42 |  $user.outsite_link 43 |
44 |
45 |
46 |
47 |
48 |
$user.name 最近创建的主题
49 | $if posts: 50 | $for post_cur in posts: 51 | $ post = post_cur['post'] 52 | $ comment_user = post_cur['comment_user'] 53 | $ node = post_cur['node'] 54 |
55 | 56 | 57 | 58 | 66 | 70 | 71 | 72 |
$:post.title 59 |
60 |
61 | $node.display_name  •  $user.name •  $helper.stamp2during(post.time) 62 | $if comment_user is not None: 63 |  •  最后回复来自 $comment_user.name 64 | 65 |
67 | $if post.comments > 0: 68 | $post.comments 69 |
73 |
74 | 75 | 76 |
77 |
78 |
79 |
80 |
$user.name 最近回复了
81 | $if comments: 82 | $ comment_len = len(comments) 83 | $ comment_len_mark = 0 84 | $ class_t = 'inner' 85 | $for comment in comments: 86 | $ comment_len_mark += 1 87 | $if comment_len_mark == comment_len: 88 | $ class_t = 'cell' 89 | $ comment_cur = comment['comment'] 90 | $ post = comment['post'] 91 | $ post_user = comment['post_user'] 92 |
93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 |
$helper.stamp2during(comment_cur.time)
回复了 $post_user.name 创建的主题 $:post.title
101 |
102 |
103 |
$:comment_cur.content
104 |
105 | 106 |
107 |
-------------------------------------------------------------------------------- /models/user_model.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | __metaclass__ = type 3 | import web 4 | import base64 5 | from models.model import * 6 | from models.site_model import * 7 | from config.config import * 8 | 9 | class user_model(model): 10 | 11 | def __init__(self): 12 | super(user_model, self).__init__('user') 13 | self.login_form = web.form.Form( 14 | web.form.Textbox('name', notnull, size=30, description="用户名", class_='sl'), 15 | web.form.Password('password', notnull, size=30, description="密码", class_='sl'), 16 | web.form.Button('登录', class_='super normal button') 17 | ) 18 | 19 | self.signup_form = web.form.Form( 20 | web.form.Textbox('name', web.form.regexp('^[a-z0-9]+$', ' 请使用小写和数字的组合'), vname, size=30, description="用户名", class_='sl', post='
请使用半角的 a-z 和数字 0-9 的组合,长度至少为3个字符'), 21 | web.form.Textbox('email', vemail, size=30, description="邮箱", class_='sl', post='
请使用真实电子邮箱注册,我们不会将你的邮箱地址分享给任何人'), 22 | web.form.Password('password', vpass, size=30, description="密码", class_='sl'), 23 | web.form.Button('注册', class_='super normal button') 24 | ) 25 | 26 | self.setting_form = web.form.Form( 27 | web.form.Textbox('name', size=30, description="用户名", class_='sl', disabled='disabled'), 28 | web.form.Textbox('email', vemail, size=30, description="邮箱", class_='sl'), 29 | web.form.Textbox('signature', web.form.regexp(r".{0,100}$", ' 请不要超过100个字符'), size=30, description="签名", class_='sl'), 30 | web.form.Textbox('outsite_link', web.form.regexp(r".{0,200}$", ' 请不要超过200个字符'), size=30, description="主页", class_='sl'), 31 | web.form.Button('保存设置', class_='super normal button') 32 | ) 33 | 34 | self.pass_form = web.form.Form( 35 | web.form.Password('origin_password', notnull, size=30, description="原密码", class_='sl'), 36 | web.form.Password('new_password', vpass, size=30, description="新密码", class_='sl'), 37 | web.form.Password('check_password', vpass, size=30, description="确认密码", class_='sl'), 38 | web.form.Button('修改密码', class_='super normal button'), 39 | validators = [web.form.Validator(" 新密码不一致", lambda i: i.new_password == i.check_password)] 40 | ) 41 | 42 | def update_session(self, user_id): 43 | user = self.get_one({'id':user_id}) 44 | web.config._session.user_id = user.id 45 | web.config._session.name = user.name 46 | web.config._session.avatar = user.avatar 47 | web.config._session.signature = user.signature 48 | web.config._session.node_favs = user.node_favs 49 | web.config._session.money = user.money 50 | web.config._session.posts = user.posts 51 | web.config._session.post_favs = user.post_favs 52 | web.config._session.user_favs = user.user_favs 53 | 54 | def set_avatar(self, filename, user_id): 55 | import Image 56 | import os 57 | path = 'static/avatar/' 58 | im = Image.open(path+'tmp/'+filename) 59 | size = im.size 60 | if size[0] > size[1]: 61 | crop_size = size[1] 62 | left = (size[0]-size[1])/2 63 | right = size[1] + left 64 | upper = 0 65 | lower = size[1] 66 | else: 67 | crop_size = size[0] 68 | left = 0 69 | right = size[0] 70 | upper = (size[1]-size[0])/2 71 | lower = size[0] + upper 72 | box = (left, upper, right, lower) 73 | region = im.crop(box) 74 | region.save(path+'tmp/'+filename) 75 | user = self.get_one({'id':user_id}) 76 | try: 77 | os.makedirs(path+'big') 78 | os.makedirs(path+'normal') 79 | os.makedirs(path+'tiny') 80 | os.remove(path+'big/'+user.avatar) 81 | os.remove(path+'normal/'+user.avatar) 82 | os.remove(path+'tiny/'+user.avatar) 83 | except: 84 | pass 85 | im = Image.open(path+'tmp/'+filename) 86 | im.thumbnail((73, 73), Image.ANTIALIAS) 87 | im.save(path+'big/'+filename, quality = 100) 88 | im.thumbnail((48, 48), Image.ANTIALIAS) 89 | im.save(path+'normal/'+filename, quality = 100) 90 | im.thumbnail((24, 24), Image.ANTIALIAS) 91 | im.save(path+'tiny/'+filename, quality = 100) 92 | del im, region 93 | os.remove(path+'tmp/'+filename) 94 | self.update({'id':user_id}, {'avatar':filename}) 95 | self.update_session(user_id) 96 | 97 | # cost 要带上符号 98 | def update_money(self, user_id, cost): 99 | sql = 'UPDATE ' + self._tb + ' SET money = money + ' + str(cost) +' WHERE id=' + str(user_id) 100 | super(user_model, self).query(sql) 101 | return self.get_one({'id':user_id}).money 102 | 103 | def auth_cookie(self, handler): 104 | try: 105 | if web.config._session.user_id is None: 106 | auth = web.cookies().get('auth') 107 | auth_list = base64.decodestring(auth).split('|') 108 | user = self.get_one({'id':auth_list[1], 'password':auth_list[0]}) 109 | if user is None: 110 | web.setcookie('auth', auth, -1) 111 | else: 112 | self.update_session(user.id) 113 | except: 114 | pass 115 | return handler() 116 | 117 | def set_cookie(self, user_id): 118 | user = self.get_one({'id':user_id}) 119 | auth = base64.encodestring(user.password+'|'+str(user.id)) 120 | web.setcookie('auth', auth, int(site_model().get_option('cookie_expires'))) 121 | -------------------------------------------------------------------------------- /tpl/post_view.html: -------------------------------------------------------------------------------- 1 | $def with (post, user, comments, form, post_fav, favs, thanks, crumb, pagination) 2 | $var title: $post.title 3 | $var widgets=['user_panel_widget'] 4 |
5 |
6 |
7 |
8 |
12 | $:crumb 13 |
14 |
15 |

$:post.title

16 | By $user.name at $helper.stamp2during(post.time), $post.views 次点击   17 |
18 |
19 |
20 | $:post.content 21 |
22 |
23 | $if session.user_id: 24 |
25 |
$post.views 次点击   26 | $if favs>0: 27 | ∙   $favs 人收藏 28 | $if post.thanks > 0: 29 | ∙   $post.thanks 人感谢 30 |
31 | $if post_fav: 32 | 取消收藏 33 | $else: 34 | 加入收藏 35 | Tweet   36 | Weibo   37 |
38 | $if thanks: 39 | 感谢已发送 40 | $else: 41 | 感谢 42 |
43 |
44 |
45 |
46 |
47 | $if post.comments > 0: 48 |
49 |
50 | $post.comments 回复  |  直到 $helper.cur_date() 51 |
52 | $ comment_num = (pagination.cur_page-1) * 100 53 | $for comment_cur in comments: 54 | $ comment_num += 1 55 | $ comment = comment_cur['comment'] 56 | $ comment_user = comment_cur['user'] 57 |
58 | 59 | 60 | 61 | 63 | 88 | 89 |
62 | 64 |
65 | $if session.user_id: 66 | $if comment_cur['thanks']: 67 |
感谢已发送
68 | $else: 69 |
70 | 感谢回复者 71 |
72 |   73 | Reply    74 | $comment_num 75 |
76 |
77 |
78 | $comment_user.name    79 | $helper.stamp2during(comment.time) 80 | $if comment.thanks > 0: 81 | ♥ $comment.thanks 82 |
83 |
84 |
85 | $:comment.content 86 |
87 |
90 |
91 |
92 | $:pagination.output() 93 |
94 |
95 | 96 | $else: 97 |
98 |
99 | 目前尚无回复 100 |
101 |
102 |
103 |
104 | $if session.user_id: 105 |
106 |
107 | 添加一条新回复 108 |
109 |
110 |
111 | $:form.render_css() 112 |
113 |
114 |
115 | 116 |   117 |
118 |
119 |
-------------------------------------------------------------------------------- /tpl/money_record.html: -------------------------------------------------------------------------------- 1 | $def with (title, records, crumb, pagination) 2 | $var title: $title 3 | $var widgets=['user_panel_widget'] 4 |
5 |
6 |
7 |
8 |
9 | 10 | $:crumb 11 |
12 |
13 | 14 | 15 | 24 | 25 |
16 | 当前账户余额 17 |
18 |
19 |
20 |
21 |
22 | $:helper.display_money(session.money) 23 |
26 |
27 |
28 | $if records: 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | $for record_cur in records: 39 | $ record = record_cur['record'] 40 | $ post = record_cur['post'] 41 | $ comment = record_cur['comment'] 42 | $ comment_user = record_cur['comment_user'] 43 | $ post_user = record_cur['post_user'] 44 | $ sender = record_cur['sender'] 45 | $ post_thanks = record_cur['post_thanks'] 46 | $ comment_thanks = record_cur['comment_thanks'] 47 | $if record_cur['type'] == 'comment_thanks': 48 | $if record.amount > 0: 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | $else: 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | $if record_cur['type'] == 'post_thanks': 65 | $if record.amount > 0: 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | $else: 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | $if record_cur['type'] == 'comment': 82 | $if record.amount > 0: 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | $else: 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | $if record_cur['type'] == 'post': 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 |
时间类型数额余额描述
$helper.stamp2time(comment_thanks.time)收到谢意$record.amount$record.balance$sender.name 感谢你发布的评论 › $:post.title
$helper.stamp2time(comment_thanks.time)发送谢意$record.amount$record.balance感谢 $comment_user.name 的回复 › $:post.title
$helper.stamp2time(post_thanks.time)收到谢意$record.amount$record.balance$sender.name 感谢你发布的主题 › $:post.title
$helper.stamp2time(post_thanks.time)发送谢意$record.amount$record.balance感谢 $post_user.name 的主题 › $:post.title
$helper.stamp2time(comment.time)主题回复收益$record.amount$record.balance收到 $comment_user.name 的回复 › $:post.title
$helper.stamp2time(comment.time)创建回复$record.amount$record.balance创建了长度为 $record.length 个字符的回复 › $:post.title
$helper.stamp2time(post.time)创建主题$record.amount$record.balance创建了长度为 $record.length 个字符的主题 › $:post.title
108 |
109 |
110 | $:pagination 111 |
112 |
113 |
-------------------------------------------------------------------------------- /controllers/post.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | import web 3 | session = web.config._session 4 | import time 5 | from config.config import render 6 | from models.post_model import * 7 | from models.post_thanks_model import * 8 | from models.node_model import * 9 | from models.user_model import * 10 | from models.user_meta_model import * 11 | from models.comment_model import * 12 | from models.comment_thanks_model import * 13 | from models.money_model import * 14 | from models.money_type_model import * 15 | from models.notify_model import * 16 | from models.notify_type_model import * 17 | from libraries.crumb import Crumb 18 | from libraries.helper import * 19 | from libraries.pagination import * 20 | 21 | # 查看单个帖子 22 | class view: 23 | 24 | def __init__(self): 25 | self.crumb = Crumb() 26 | 27 | def POST(self, id): 28 | raise web.SeeOther('/post/' + str(id)) 29 | 30 | def GET(self, id): 31 | limit = 10 32 | post_model().add_view(id) 33 | post = post_model().get_one({'id':id}) 34 | if post is None: 35 | self.crumb.append('主题未找到') 36 | return render.post_nf('主题未找到', self.crumb.output()) 37 | else: 38 | post_fav = False 39 | if session.user_id: 40 | if user_meta_model().get_one({'user_id':session.user_id, 'meta_key':'post_fav', 'meta_value':post.id}): 41 | post_fav = True 42 | favs = user_meta_model().count_meta({'meta_key':'post_fav','meta_value':id}) 43 | node = node_model().get_one({'id':post.node_id}) 44 | user = user_model().get_one({'id':post.user_id}) 45 | #return user.name 46 | self.crumb.append(node.display_name, '/node/'+node.name) 47 | thanks = False 48 | if session.user_id is not None: 49 | if post_thanks_model().get_one({'user_id':session.user_id, 'post_id':post.id}): 50 | thanks = True 51 | condition = {'post_id' : post.id} 52 | # Pagination 53 | total = comment_model().count_table(condition) 54 | pagination = Pagination('/post/'+str(post.id), total, limit = 100) 55 | page = pagination.true_page(web.input(p=1)['p']) 56 | comments_result = comment_model().get_all(condition, order = 'time ASC', limit = 100, offset = (page-1)*100) 57 | comments = [] 58 | if comments_result is not None: 59 | for comment_result in comments_result: 60 | comment_user = user_model().get_one({'id':comment_result.user_id}) 61 | comment_thanks = False 62 | if session.user_id is not None: 63 | if comment_thanks_model().get_one({'user_id':session.user_id, 'comment_id':comment_result.id}): 64 | comment_thanks = True 65 | comments.append({'comment':comment_result, 'user':comment_user, 'thanks':comment_thanks}) 66 | form = comment_model().form 67 | return render.post_view(post, user, comments, form, post_fav, favs, thanks, self.crumb.output(), pagination) 68 | 69 | # 创建帖子 70 | class create: 71 | 72 | def __init__(self): 73 | self.crumb = Crumb() 74 | self.form = post_model().form 75 | 76 | def GET(self, node_name): 77 | if session.user_id is None: 78 | raise web.SeeOther('/login?next=/post/create/' + node_name) 79 | conditions = {'name' : node_name} 80 | node = node_model().get_one(conditions) 81 | self.crumb.append(node.display_name, '/node/'+node.name) 82 | self.crumb.append('创建新主题') 83 | if node is None: 84 | self.crumb.claer() 85 | return render.not_found('节点未找到', '节点未找到') 86 | title = '创建主题' 87 | return render.create_post(self.form, title, self.crumb.output()) 88 | 89 | def POST(self, node_name): 90 | if session.user_id is None: 91 | raise web.SeeOther('/login?next=/post/create' + node_name) 92 | conditions = {'name' : node_name} 93 | node = node_model().get_one(conditions) 94 | if node is None: 95 | return render.not_found('节点未找到', '节点未找到') 96 | if not self.form.validates(): 97 | return render.create_post(self.form, '创建失败, 请重创:D', self.crumb.output()) 98 | user_model().update_session(session.user_id) 99 | length, cost = money_model().cal_post(self.form.d.content) 100 | if session.money < cost: 101 | self.crumb.append('财富不够') 102 | return render.no_money('财富不够', '你的财富值不够,不能创建改主题 :(', self.crumb.output()) 103 | title = strip_tags(self.form.d.title) 104 | content = html2db(self.form.d.content) 105 | content, receiver_list = notify_model().convert_content(content) 106 | create_time = time.time() 107 | post_id = post_model().insert({'title' : title, 'content' : content, 'node_id' : node.id, 'time' : create_time, 'last_update':create_time, 'user_id' : session.user_id}) 108 | # money 109 | money_type_id = money_type_model().get_one({'name':'post'})['id'] 110 | money_model().insert({'user_id':session.user_id, 'money_type_id':money_type_id, 'amount':-cost, 'length':length, 'balance':user_model().update_money(session.user_id, -cost), 'foreign_id':post_id}) 111 | # notify 112 | receiver_list = list_diff(receiver_list, [session.name]) 113 | notify_model().insert_notify(session.user_id, receiver_list, 'post_at', post_id) 114 | user_model().update_session(session.user_id) 115 | raise web.seeother('/post/' + str(post_id)) 116 | 117 | # 收藏帖子 118 | class fav: 119 | 120 | def __init__(self): 121 | self.crumb = Crumb() 122 | 123 | def GET(self, post_id): 124 | post = post_model().get_one({'id':post_id}) 125 | if post is None: 126 | self.crumb.append('主题未找到') 127 | return render.post_nf('主题未找到', self.crumb.output()) 128 | if session.user_id is None: 129 | raise web.SeeOther('/login?next=/post/fav/'+post_id) 130 | user_meta_model().unique_insert({'user_id':session.user_id, 'meta_key':'post_fav', 'meta_value':post_id}) 131 | user_model().update({'id':session.user_id}, {'post_favs':user_meta_model().count_meta({'user_id':session.user_id, 'meta_key':'post_fav'})}) 132 | user_model().update_session(session.user_id) 133 | raise web.SeeOther('/post/' + post_id) 134 | 135 | class unfav: 136 | 137 | def GET(self, post_id): 138 | if session.user_id is None: 139 | raise web.SeeOther('/login?next=/post/unfav/'+post_id) 140 | user_meta_model().delete({'user_id':session.user_id, 'meta_key':'post_fav','meta_value':post_id}) 141 | user_model().update({'id':session.user_id}, {'post_favs':user_meta_model().count_meta({'user_id':session.user_id, 'meta_key':'post_fav'})}) 142 | user_model().update_session(session.user_id) 143 | raise web.SeeOther('/post/'+post_id) 144 | 145 | class thanks: 146 | def POST(self): 147 | import json 148 | json_dict = {'success':0, 'msg':'', 'script':''} 149 | post_id = web.input(post_id=None)['post_id'] 150 | post = post_model().get_one({'id':post_id}) 151 | if post_id and post: 152 | if session.user_id is None: 153 | json_dict['msg'] = '你要先登录的亲' 154 | json_dict['script'] = 'location.href=\'/login?next=/post/'+post.id+'\'' 155 | elif post.user_id != session.user_id: 156 | post_thanks_id = post_thanks_model().unique_insert({'user_id':session.user_id, 'post_id':post_id}) 157 | if post_thanks_id: 158 | post_thanks_model().update({'id':post_thanks_id}, {'time':int(time.time())}) 159 | cost = money_model().cal_thanks() 160 | money_type_id = money_type_model().get_one({'name':'post_thanks'})['id'] 161 | money_model().insert({'user_id':session.user_id, 'money_type_id':money_type_id, 'amount':-cost, 'balance':user_model().update_money(session.user_id, -cost), 'foreign_id':post_thanks_id}) 162 | money_model().insert({'user_id':post.user_id, 'money_type_id':money_type_id, 'amount':cost, 'foreign_id':post_thanks_id, 'balance':user_model().update_money(post.user_id, cost)}) 163 | post_model().count_thanks(post_id) 164 | user_model().update_session(session.user_id) 165 | json_dict['success'] = 1 166 | else: 167 | json_dict['msg'] = '你已经感谢过了不是吗?' 168 | else: 169 | json_dict['msg'] = '你不能感谢你自己不是吗?' 170 | else: 171 | json_dict['message'] = '评论不存在' 172 | return json.dumps(json_dict) -------------------------------------------------------------------------------- /controllers/admin.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | __metaclass__ = type 3 | import web 4 | session = web.config._session 5 | from config.config import * 6 | from models.node_model import * 7 | from models.cat_model import * 8 | from models.post_model import * 9 | from models.user_model import * 10 | from models.comment_model import * 11 | from models.site_model import * 12 | from libraries.crumb import Crumb 13 | 14 | class admin: 15 | def __init__(self): 16 | if session.user_id != 1: 17 | raise web.SeeOther('/') 18 | self.crumb = Crumb() 19 | self.crumb.append('后台', '/admin') 20 | 21 | class index(admin): 22 | 23 | def GET(self): 24 | cat_result = cat_model().get_all() 25 | cats = [] 26 | for cat in cat_result: 27 | node_total = node_model().count_table({'category_id':cat.id}) 28 | cats.append({'cat':cat, 'node_total':node_total}) 29 | return admin_render.index('后台', cats, self.crumb.output()) 30 | 31 | class site(admin): 32 | 33 | def __init__(self): 34 | super(site, self).__init__() 35 | self.form = site_model().form 36 | self.site = site_model().get_options() 37 | self.form.title.set_value(self.site['title']) 38 | self.form.description.set_value(self.site['description']) 39 | self.form.site_url.set_value(self.site['site_url']) 40 | self.form.cookie_expires.set_value(self.site['cookie_expires']) 41 | 42 | def GET(self): 43 | self.crumb.append('站点设置') 44 | return admin_render.site('站点设置', self.crumb.output(), self.form) 45 | 46 | def POST(self): 47 | if self.form.validates(): 48 | site_model().update({'key':'title'}, {'value':self.form.d.title}) 49 | site_model().update({'key':'description'}, {'value':self.form.d.description}) 50 | site_model().update({'key':'site_url'}, {'value':self.form.d.site_url}) 51 | site_model().update({'key':'cookie_expires'}, {'value':self.form.d.cookie_expires}) 52 | # 不知道这里为什么还要clear一次才能保证crumb的干净 53 | 54 | raise web.SeeOther('/admin/site') 55 | else: 56 | self.crumb.append('站点设置') 57 | return admin_render.site('站点设置', self.crumb.output(), self.form) 58 | 59 | 60 | class cat(admin): 61 | 62 | def __init__(self): 63 | super(cat, self).__init__() 64 | self.form = cat_model().modify_form 65 | 66 | def GET(self, cat_name): 67 | cat = cat_model().get_one({'name':cat_name}) 68 | if cat is None: 69 | self.crumb.append('分类不存在') 70 | return admin_render.cat_nf('分类不存在', self.crumb.output()) 71 | else: 72 | self.crumb.append(cat.display_name) 73 | nodes = node_model().get_all({'category_id':cat.id}) 74 | self.form.name.set_value(cat.name) 75 | self.form.display_name.set_value(cat.display_name) 76 | self.form.description.set_value(cat.description) 77 | return admin_render.cat_view(cat.display_name, self.crumb.output(), cat, self.form, nodes) 78 | 79 | def POST(self, cat_name): 80 | cat = cat_model().get_one({'name':cat_name}) 81 | if cat is None: 82 | self.crumb.append('分类不存在') 83 | return admin_render.cat_nf('分类不存在', self.crumb.output()) 84 | else: 85 | if self.form.validates(): 86 | cat_model().update({'name':cat.name}, {'display_name':self.form.d.display_name, 'description':self.form.d.description}) 87 | 88 | web.SeeOther('/admin/cat/'+cat.name) 89 | else: 90 | self.form.name.set_value(cat.name) 91 | self.form.display_name.set_value(cat.display_name) 92 | self.form.description.set_value(cat.description) 93 | 94 | web.SeeOther('/admin/cat/'+cat.name) 95 | 96 | class create_cat(admin): 97 | 98 | def __init__(self): 99 | super(create_cat, self).__init__() 100 | self.form = cat_model().create_form 101 | 102 | def GET(self): 103 | self.crumb.append('添加新分类') 104 | return admin_render.create_cat('添加新分类', self.crumb.output(), self.form) 105 | def POST(self): 106 | if self.form.validates(): 107 | if cat_model().unique_insert({'name':self.form.d.name}): 108 | # 为了保证不插入空的display_name的分类,故此 109 | try: 110 | cat_model().update({'name':self.form.d.name}, {'display_name':self.form.d.display_name, 'description':self.form.d.description}) 111 | except: 112 | cat_model().delete({'name':self.form.d.name}) 113 | 114 | web.SeeOther('/admin/cat/'+self.form.d.name) 115 | else: 116 | return admin_render.create_cat('分类名已存在', self.crumb.output(), self.form) 117 | 118 | class node(admin): 119 | 120 | def __init__(self): 121 | super(node, self).__init__() 122 | self.form = node_model().modify_form 123 | 124 | def GET(self, node_name): 125 | node = node_model().get_one({'name':node_name}) 126 | if node is None: 127 | return admin_render.node_nf('节点不存在', self.crumb.output()) 128 | ##return node 129 | cat = cat_model().get_one({'id':node.category_id}) 130 | self.form.name.set_value(node.name) 131 | self.form.display_name.set_value(node.display_name) 132 | self.form.description.set_value(node.description) 133 | self.crumb.append(cat.display_name, '/admin/cat/'+cat.name) 134 | self.crumb.append(node.display_name) 135 | return admin_render.node_view(node.display_name, self.crumb.output(), node, self.form) 136 | 137 | def POST(self, node_name): 138 | node = node_model().get_one({'name':node_name}) 139 | if node is None: 140 | return admin_render.node_nf('节点不存在', self.crumb.output()) 141 | if self.form.validates(): 142 | node_model().update({'name':node.name}, {'display_name':self.form.d.display_name, 'description':self.form.d.description}) 143 | raise web.SeeOther('/admin/node/'+node.name) 144 | 145 | class set_node_icon(admin): 146 | 147 | def GET(self, node_name): 148 | node = node_model().get_one({'name':node_name}) 149 | if node is None: 150 | return admin_render.node_nf('节点不存在', self.crumb.output()) 151 | cat = cat_model().get_one({'id':node.category_id}) 152 | self.crumb.append(cat.display_name, '/admin/cat/'+cat.name) 153 | self.crumb.append(node.display_name, '/admin/node/'+node.name) 154 | self.crumb.append('设置节点图标') 155 | return admin_render.set_node_icon('设置节点图标', self.crumb.output(), node) 156 | 157 | def POST(self, node_name): 158 | node = node_model().get_one({'name':node_name}) 159 | if node is None: 160 | return admin_render.node_nf('节点不存在', self.crumb.output()) 161 | cat = cat_model().get_one({'id':node.category_id}) 162 | self.crumb.append(cat.display_name, '/admin/cat/'+cat.name) 163 | self.crumb.append(node.display_name, '/admin/node/'+node.name) 164 | self.crumb.append('设置节点图标') 165 | import cgi 166 | import os 167 | cgi.maxlen = 2 * 1024 * 1024 # 2MB 168 | try: 169 | x = web.input(icon={}) 170 | except ValueError: 171 | return admin_render.set_node_icon('设置节点图标', self.crumb.output(), node, ' <<超过大小限制') 172 | if 'icon' in x: 173 | #客户端为windows时注意 174 | filepath=x.icon.filename.replace('\\','/') 175 | #获取文件名 176 | filename=filepath.split('/')[-1] 177 | #获取后缀 178 | ext = filename.split('.', 1)[1].lower() 179 | ext_allow = ('jpg', 'png', 'gif', 'jpeg') 180 | #判断文件后缀名 181 | if ext in ext_allow: 182 | #要上传的路径 183 | filedir = 'static/icons/tmp/' 184 | try: 185 | os.makedirs('static/icons/tmp') 186 | except: 187 | pass 188 | filename = str(node.id) +'.'+ext 189 | if os.path.exists(filedir+filename): 190 | os.remove(filedir+filename) 191 | fout = open(filedir + filename, 'wb') 192 | fout.write(x.icon.file.read()) 193 | fout.close() 194 | node_model().set_icon(filename, node.id) 195 | error = False 196 | else: 197 | message = ' <<请上传指定格式文件' 198 | error = True 199 | if error: 200 | return admin_render.set_node_icon('设置节点图标', self.crumb.output(), node, message) 201 | else: 202 | 203 | raise web.SeeOther('/admin/node/icon/'+node.name) 204 | 205 | class create_node(admin): 206 | 207 | def __init__(self): 208 | super(create_node, self).__init__() 209 | self.form = node_model().create_form 210 | 211 | def GET(self, cat_name): 212 | cat = cat_model().get_one({'name':cat_name}) 213 | if cat is None: 214 | self.crumb.append('分类不存在') 215 | return admin_render.cat_nf('分类不存在', self.crumb.output()) 216 | self.crumb.append(cat.name, '/admin/cat/'+cat.name) 217 | self.crumb.append('添加新节点') 218 | return admin_render.create_node('添加新节点', self.crumb.output(), cat, self.form) 219 | 220 | def POST(self, cat_name): 221 | cat = cat_model().get_one({'name':cat_name}) 222 | if cat is None: 223 | self.crumb.append('分类不存在') 224 | return admin_render.cat_nf('分类不存在', self.crumb.output()) 225 | if self.form.validates(): 226 | if node_model().unique_insert({'name':self.form.d.name}): 227 | # 为了保证不插入空的display_name的节点,故此 228 | try: 229 | node_model().update({'name':self.form.d.name}, {'category_id':cat.id, 'display_name':self.form.d.display_name, 'description':self.form.d.description}) 230 | except: 231 | node_model().delete({'name':self.form.d.name}) 232 | 233 | web.SeeOther('/admin/node/'+self.form.d.name) 234 | else: 235 | return admin_render.create_cat('节点名已存在', self.crumb.output(), self.form) 236 | else: 237 | return admin_render.create_node('添加新节点', self.crumb.output(), cat, self.form) 238 | -------------------------------------------------------------------------------- /post_bar_init.sql: -------------------------------------------------------------------------------- 1 | # ************************************************************ 2 | # Sequel Pro SQL dump 3 | # Version 3408 4 | # 5 | # http://www.sequelpro.com/ 6 | # http://code.google.com/p/sequel-pro/ 7 | # 8 | # Host: 127.0.0.1 (MySQL 5.5.28) 9 | # Database: post_bar_init 10 | # Generation Time: 2012-12-02 13:16:35 +0000 11 | # ************************************************************ 12 | 13 | 14 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 15 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 16 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 17 | /*!40101 SET NAMES utf8 */; 18 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 19 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 20 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 21 | 22 | 23 | # Dump of table category 24 | # ------------------------------------------------------------ 25 | 26 | DROP TABLE IF EXISTS `category`; 27 | 28 | CREATE TABLE `category` ( 29 | `id` int(10) NOT NULL AUTO_INCREMENT, 30 | `name` varchar(45) NOT NULL COMMENT '分类名', 31 | `display_name` varchar(45) NOT NULL COMMENT '显示名', 32 | `description` text NOT NULL COMMENT '分类描述', 33 | PRIMARY KEY (`id`), 34 | UNIQUE KEY `name` (`name`) 35 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='节点分类'; 36 | 37 | 38 | 39 | # Dump of table comment 40 | # ------------------------------------------------------------ 41 | 42 | DROP TABLE IF EXISTS `comment`; 43 | 44 | CREATE TABLE `comment` ( 45 | `id` int(10) NOT NULL AUTO_INCREMENT, 46 | `user_id` int(10) DEFAULT NULL, 47 | `post_id` int(10) DEFAULT NULL COMMENT '帖子id', 48 | `content` text COMMENT '内容', 49 | `time` int(10) DEFAULT NULL COMMENT '评论时间', 50 | `thanks` int(10) NOT NULL DEFAULT '0', 51 | PRIMARY KEY (`id`), 52 | KEY `comment_user` (`user_id`), 53 | KEY `comment_post` (`post_id`), 54 | CONSTRAINT `comment_post` FOREIGN KEY (`post_id`) REFERENCES `post` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, 55 | CONSTRAINT `comment_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION 56 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='帖子评论'; 57 | 58 | 59 | 60 | # Dump of table comment_thanks 61 | # ------------------------------------------------------------ 62 | 63 | DROP TABLE IF EXISTS `comment_thanks`; 64 | 65 | CREATE TABLE `comment_thanks` ( 66 | `id` int(10) NOT NULL AUTO_INCREMENT, 67 | `user_id` int(10) NOT NULL, 68 | `comment_id` int(10) NOT NULL, 69 | `time` int(10) NOT NULL, 70 | PRIMARY KEY (`id`), 71 | KEY `fk_comment_thanks_1` (`user_id`), 72 | KEY `fk_comment_thanks_2` (`comment_id`), 73 | CONSTRAINT `fk_comment_thanks_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`), 74 | CONSTRAINT `fk_comment_thanks_2` FOREIGN KEY (`comment_id`) REFERENCES `comment` (`id`) 75 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 76 | 77 | 78 | 79 | # Dump of table money 80 | # ------------------------------------------------------------ 81 | 82 | DROP TABLE IF EXISTS `money`; 83 | 84 | CREATE TABLE `money` ( 85 | `id` int(10) NOT NULL AUTO_INCREMENT, 86 | `user_id` int(10) NOT NULL, 87 | `money_type_id` int(10) NOT NULL COMMENT '收支类型', 88 | `length` int(10) NOT NULL DEFAULT '0' COMMENT '长度', 89 | `amount` float NOT NULL DEFAULT '0' COMMENT '金额', 90 | `balance` float NOT NULL DEFAULT '0' COMMENT '余额', 91 | `foreign_id` int(11) DEFAULT '0' COMMENT '帖子/评论/用户 id', 92 | PRIMARY KEY (`id`), 93 | KEY `money_money_type` (`money_type_id`), 94 | KEY `money_user` (`user_id`), 95 | CONSTRAINT `money_money_type` FOREIGN KEY (`money_type_id`) REFERENCES `money_type` (`id`), 96 | CONSTRAINT `money_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) 97 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='财富收支表'; 98 | 99 | 100 | 101 | # Dump of table money_option 102 | # ------------------------------------------------------------ 103 | 104 | DROP TABLE IF EXISTS `money_option`; 105 | 106 | CREATE TABLE `money_option` ( 107 | `id` int(10) NOT NULL AUTO_INCREMENT, 108 | `key` varchar(50) NOT NULL, 109 | `value` varchar(100) DEFAULT NULL, 110 | `comment` varchar(50) DEFAULT NULL, 111 | PRIMARY KEY (`id`), 112 | KEY `key` (`key`) 113 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='全站配置'; 114 | 115 | LOCK TABLES `money_option` WRITE; 116 | /*!40000 ALTER TABLE `money_option` DISABLE KEYS */; 117 | 118 | INSERT INTO `money_option` (`id`, `key`, `value`, `comment`) 119 | VALUES 120 | (1,'post_cost','20','发帖基础花费财富'), 121 | (2,'post_cost_add','1','帖子每增加100字符花费'), 122 | (3,'post_length','200','帖子基础字符'), 123 | (4,'comment_length','100','评论基础字符'), 124 | (5,'comment_cost','5','评论基础花费'), 125 | (6,'comment_cost_add','1','评论每增加100字花费'), 126 | (7,'thanks_cost','10','感谢花费'); 127 | 128 | /*!40000 ALTER TABLE `money_option` ENABLE KEYS */; 129 | UNLOCK TABLES; 130 | 131 | 132 | # Dump of table money_type 133 | # ------------------------------------------------------------ 134 | 135 | DROP TABLE IF EXISTS `money_type`; 136 | 137 | CREATE TABLE `money_type` ( 138 | `id` int(10) NOT NULL AUTO_INCREMENT, 139 | `name` varchar(50) DEFAULT NULL, 140 | `comment` varchar(50) DEFAULT NULL, 141 | PRIMARY KEY (`id`) 142 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='财富的获取或支出类型'; 143 | 144 | LOCK TABLES `money_type` WRITE; 145 | /*!40000 ALTER TABLE `money_type` DISABLE KEYS */; 146 | 147 | INSERT INTO `money_type` (`id`, `name`, `comment`) 148 | VALUES 149 | (1,'invite','邀请'), 150 | (2,'post','主题'), 151 | (3,'comment','评论'), 152 | (4,'post_thanks','感谢主题'), 153 | (5,'comment_thanks','感谢评论'); 154 | 155 | /*!40000 ALTER TABLE `money_type` ENABLE KEYS */; 156 | UNLOCK TABLES; 157 | 158 | 159 | # Dump of table node 160 | # ------------------------------------------------------------ 161 | 162 | DROP TABLE IF EXISTS `node`; 163 | 164 | CREATE TABLE `node` ( 165 | `id` int(10) NOT NULL AUTO_INCREMENT, 166 | `category_id` int(10) DEFAULT NULL, 167 | `name` varchar(45) NOT NULL COMMENT '节点名', 168 | `display_name` varchar(45) NOT NULL COMMENT '显示名', 169 | `description` text COMMENT '描述', 170 | `icon` varchar(50) DEFAULT NULL COMMENT '图标', 171 | PRIMARY KEY (`id`), 172 | UNIQUE KEY `name` (`name`), 173 | KEY `node_category` (`category_id`), 174 | CONSTRAINT `node_category` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION 175 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='所有节点'; 176 | 177 | 178 | 179 | # Dump of table notify 180 | # ------------------------------------------------------------ 181 | 182 | DROP TABLE IF EXISTS `notify`; 183 | 184 | CREATE TABLE `notify` ( 185 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 186 | `user_id` int(11) NOT NULL, 187 | `receiver` int(11) NOT NULL, 188 | `type_id` int(11) unsigned NOT NULL, 189 | `foreign_id` int(11) NOT NULL, 190 | `unread` tinyint(1) NOT NULL DEFAULT '1', 191 | PRIMARY KEY (`id`), 192 | KEY `nofity_user` (`user_id`), 193 | KEY `notify_user_receiver` (`receiver`), 194 | KEY `notify__notify_type` (`type_id`), 195 | CONSTRAINT `nofity_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`), 196 | CONSTRAINT `notify_user_receiver` FOREIGN KEY (`receiver`) REFERENCES `user` (`id`), 197 | CONSTRAINT `notify__notify_type` FOREIGN KEY (`type_id`) REFERENCES `notify_type` (`id`) 198 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 199 | 200 | 201 | 202 | # Dump of table notify_type 203 | # ------------------------------------------------------------ 204 | 205 | DROP TABLE IF EXISTS `notify_type`; 206 | 207 | CREATE TABLE `notify_type` ( 208 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 209 | `name` varchar(50) NOT NULL DEFAULT '', 210 | `comment` varchar(50) DEFAULT NULL, 211 | PRIMARY KEY (`id`) 212 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 213 | 214 | LOCK TABLES `notify_type` WRITE; 215 | /*!40000 ALTER TABLE `notify_type` DISABLE KEYS */; 216 | 217 | INSERT INTO `notify_type` (`id`, `name`, `comment`) 218 | VALUES 219 | (1,'comment','收到评论'), 220 | (2,'post_at','在帖子中提及'), 221 | (3,'comment_at','在回复中提及'); 222 | 223 | /*!40000 ALTER TABLE `notify_type` ENABLE KEYS */; 224 | UNLOCK TABLES; 225 | 226 | 227 | # Dump of table post 228 | # ------------------------------------------------------------ 229 | 230 | DROP TABLE IF EXISTS `post`; 231 | 232 | CREATE TABLE `post` ( 233 | `id` int(10) NOT NULL AUTO_INCREMENT, 234 | `user_id` int(10) DEFAULT NULL, 235 | `node_id` int(10) DEFAULT NULL COMMENT '节点id', 236 | `title` text NOT NULL COMMENT '标贴', 237 | `content` text NOT NULL, 238 | `time` int(10) NOT NULL COMMENT '发帖时间', 239 | `views` int(10) DEFAULT '0' COMMENT '围观数', 240 | `comments` int(10) DEFAULT '0' COMMENT '评论数', 241 | `thanks` int(10) NOT NULL DEFAULT '0', 242 | `last_update` int(10) DEFAULT NULL COMMENT '最后更新时间(发帖,评论)', 243 | PRIMARY KEY (`id`), 244 | KEY `post_user` (`user_id`), 245 | KEY `post_node` (`node_id`), 246 | CONSTRAINT `post_node` FOREIGN KEY (`node_id`) REFERENCES `node` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, 247 | CONSTRAINT `post_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION 248 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='所有帖子'; 249 | 250 | 251 | 252 | # Dump of table post_thanks 253 | # ------------------------------------------------------------ 254 | 255 | DROP TABLE IF EXISTS `post_thanks`; 256 | 257 | CREATE TABLE `post_thanks` ( 258 | `id` int(10) NOT NULL AUTO_INCREMENT, 259 | `user_id` int(10) NOT NULL, 260 | `post_id` int(10) NOT NULL, 261 | `time` int(10) NOT NULL, 262 | PRIMARY KEY (`id`), 263 | KEY `fk_post_thanks_1` (`user_id`), 264 | KEY `fk_post_thanks_2` (`post_id`), 265 | CONSTRAINT `fk_post_thanks_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION, 266 | CONSTRAINT `fk_post_thanks_2` FOREIGN KEY (`post_id`) REFERENCES `post` (`id`) 267 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 268 | 269 | 270 | 271 | # Dump of table site 272 | # ------------------------------------------------------------ 273 | 274 | DROP TABLE IF EXISTS `site`; 275 | 276 | CREATE TABLE `site` ( 277 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 278 | `key` varchar(50) DEFAULT NULL, 279 | `value` text, 280 | PRIMARY KEY (`id`) 281 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 282 | 283 | LOCK TABLES `site` WRITE; 284 | /*!40000 ALTER TABLE `site` DISABLE KEYS */; 285 | 286 | INSERT INTO `site` (`id`, `key`, `value`) 287 | VALUES 288 | (1,'title','Post Bar你妹'), 289 | (2,'description','Web.py 的一个贴吧'), 290 | (3,'cookie_expires','604800'), 291 | (4,'site_url','127.0.0.1:8888'); 292 | 293 | /*!40000 ALTER TABLE `site` ENABLE KEYS */; 294 | UNLOCK TABLES; 295 | 296 | 297 | # Dump of table user 298 | # ------------------------------------------------------------ 299 | 300 | DROP TABLE IF EXISTS `user`; 301 | 302 | CREATE TABLE `user` ( 303 | `id` int(10) NOT NULL AUTO_INCREMENT, 304 | `name` varchar(45) NOT NULL, 305 | `email` varchar(45) NOT NULL COMMENT '邮箱', 306 | `password` varchar(45) DEFAULT NULL COMMENT '密码', 307 | `gender` int(1) DEFAULT '1' COMMENT '性别', 308 | `regist_time` int(10) DEFAULT NULL COMMENT '注册时间', 309 | `signature` char(100) DEFAULT NULL COMMENT '签名', 310 | `avatar` varchar(45) DEFAULT NULL COMMENT '头像', 311 | `outsite_link` text COMMENT '主页', 312 | `posts` int(10) DEFAULT '0' COMMENT '发表帖子数', 313 | `money` float DEFAULT '2000' COMMENT '财富', 314 | `node_favs` int(10) DEFAULT '0' COMMENT '收藏节点数', 315 | `post_favs` int(10) DEFAULT '0' COMMENT '收藏帖子数', 316 | `user_favs` int(10) DEFAULT '0' COMMENT '关注成员数', 317 | `auth` varchar(5) NOT NULL DEFAULT '' COMMENT '组合加密字符串', 318 | PRIMARY KEY (`id`), 319 | UNIQUE KEY `email_UNIQUE` (`email`), 320 | UNIQUE KEY `name_UNIQUE` (`name`) 321 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户'; 322 | 323 | 324 | 325 | # Dump of table user_meta 326 | # ------------------------------------------------------------ 327 | 328 | DROP TABLE IF EXISTS `user_meta`; 329 | 330 | CREATE TABLE `user_meta` ( 331 | `id` int(10) NOT NULL AUTO_INCREMENT, 332 | `user_id` int(10) NOT NULL, 333 | `meta_key` varchar(50) NOT NULL, 334 | `meta_value` varchar(50) NOT NULL, 335 | PRIMARY KEY (`id`), 336 | KEY `user_meta_user` (`user_id`), 337 | CONSTRAINT `user_meta_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) 338 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户扩展表'; 339 | 340 | 341 | 342 | 343 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; 344 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; 345 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; 346 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 347 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 348 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 349 | -------------------------------------------------------------------------------- /controllers/user.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf8 -- 2 | import web 3 | session = web.config._session 4 | import hashlib 5 | import time 6 | import random 7 | import string 8 | from config.config import render 9 | from models.user_model import * 10 | from models.user_meta_model import * 11 | from models.post_model import * 12 | from models.post_thanks_model import * 13 | from models.comment_thanks_model import * 14 | from models.node_model import * 15 | from models.comment_model import * 16 | from models.money_model import * 17 | from models.money_type_model import * 18 | from libraries.error import * 19 | from libraries.crumb import * 20 | from libraries.pagination import * 21 | 22 | class login: 23 | 24 | def __init__(self): 25 | if session.user_id: 26 | raise web.SeeOther('/') 27 | self.title = '登录' 28 | self.crumb = Crumb() 29 | self.crumb.append('登录') 30 | self.form = user_model().login_form 31 | 32 | def GET(self): 33 | return render.login(self.form, self.title, self.crumb.output()) 34 | 35 | def POST(self): 36 | if not self.form.validates(): 37 | return render.login(self.form, '登录失败,请重登', self.crumb.output()) 38 | condition = {'name' : self.form.d.name} 39 | # MD5加密 密码 40 | #condition['password'] = hashlib.md5(condition['password']).hexdigest() 41 | user = user_model().get_one(condition) 42 | if user is None: 43 | return render.login(self.form, '用户名不存在', self.crumb.output()) 44 | auth_from_form = hashlib.md5(hashlib.md5(self.form.d.password).hexdigest() + user.auth).hexdigest() 45 | if auth_from_form != user.password: 46 | return render.login(self.form, '密码错误', self.crumb.output()) 47 | user_model().update_session(user.id) 48 | user_model().set_cookie(user.id) 49 | data = web.input(); 50 | try: 51 | if data['next'] is not None: 52 | raise web.SeeOther(data['next']) 53 | else: 54 | raise web.SeeOther('/') 55 | except KeyError: 56 | raise web.SeeOther('/') 57 | 58 | class signup: 59 | 60 | def __init__(self): 61 | self.form = user_model().signup_form 62 | self.crumb = Crumb() 63 | self.crumb.append('注册') 64 | 65 | def GET(self): 66 | title = '注册' 67 | return render.signup(self.form, title, self.crumb.output()) 68 | 69 | def POST(self): 70 | if not self.form.validates(): 71 | return render.signup(self.form, '注册失败,请重注', self.crumb.output()) 72 | try: 73 | condition = {'name':self.form.d.name} 74 | user = user_model().get_one(condition) 75 | # 对密码进行 md5 加密 76 | auth = string.join(random.sample(['z','y','x','w','v','u','t','s','r','q','p','o','n','m','l','k','j','i','h','g','f','e','d','c','b','a'], 5)).replace(' ','') 77 | password = hashlib.md5(hashlib.md5(self.form.d.password).hexdigest() + auth).hexdigest() 78 | #password = hashlib.md5(self.form.d.password).hexdigest() 79 | if user is not None: 80 | raise ValueExistsError('用户名已经存在') 81 | condition = {'email' : self.form.d.email} 82 | user = user_model().get_one(condition) 83 | if user is not None: 84 | raise ValueExistsError('邮箱已经存在') 85 | user_model().insert({'name' : self.form.d.name, 'email' : self.form.d.email, 'password' : password, 'regist_time' : time.time(), 'auth' : auth}) 86 | except ValueExistsError, x: 87 | return render.signup(self.form, x.message, self.crumb.output()) 88 | raise web.SeeOther('/') 89 | 90 | # 注销 91 | class logout: 92 | 93 | def GET(self): 94 | session.kill() 95 | web.setcookie('auth', '', -1) 96 | raise web.SeeOther('/') 97 | 98 | # 设置 99 | class settings: 100 | 101 | def __init__(self): 102 | if session.user_id is None: 103 | raise web.SeeOther('/login?next=/settings') 104 | self.setting_form = user_model().setting_form 105 | self.pass_form = user_model().pass_form 106 | self.crumb = Crumb() 107 | 108 | def GET(self): 109 | self.crumb.append('设置') 110 | user = user_model().get_one({'id':session.user_id}) 111 | self.setting_form.name.set_value(user.name) 112 | self.setting_form.email.set_value(user.email) 113 | self.setting_form.signature.set_value(user.signature) 114 | self.setting_form.outsite_link.set_value(user.outsite_link) 115 | return render.settings('设置', user, self.setting_form, self.pass_form, self.crumb.output()) 116 | def POST(self): 117 | self.crumb.append('设置') 118 | user = user_model().get_one({'id':session.user_id}) 119 | self.setting_form.name.set_value(user.name) 120 | if not self.setting_form.validates(): 121 | self.setting_form.name.set_value(user.name) 122 | self.setting_form.email.set_value(user.email) 123 | self.setting_form.email.set_value(user.signature) 124 | self.setting_form.email.set_value(user.outsite_link) 125 | return render.settings('设置', user, self.setting_form, self.pass_form, self.crumb.output()) 126 | else: 127 | user_model().update({'id':user.id}, {'email':self.setting_form.d.email, 'signature':self.setting_form.d.signature, 'outsite_link':self.setting_form.d.outsite_link.replace('http://', '').replace('https://', '')}) 128 | 129 | raise web.SeeOther('/settings') 130 | 131 | class password: 132 | 133 | def __init__(self): 134 | if session.user_id is None: 135 | raise web.SeeOther('/login?next=/settings/password') 136 | self.form = user_model().pass_form 137 | self.crumb = Crumb() 138 | def GET(self): 139 | self.crumb.append('设置', '/settings') 140 | self.crumb.append('修改密码') 141 | return render.password('修改密码', self.crumb.output(), self.form) 142 | def POST(self): 143 | self.crumb.append('设置', '/settings') 144 | self.crumb.append('修改密码') 145 | user = user_model().get_one({'id':session.user_id}) 146 | if self.form.validates(): 147 | password = hashlib.md5(hashlib.md5(self.form.d.origin_password).hexdigest() + user.auth).hexdigest() 148 | if user.password == password: 149 | auth = string.join(random.sample(['z','y','x','w','v','u','t','s','r','q','p','o','n','m','l','k','j','i','h','g','f','e','d','c','b','a'], 5)).replace(' ','') 150 | new_password = hashlib.md5(hashlib.md5(self.form.d.new_password).hexdigest() + auth).hexdigest() 151 | user_model().update({'id':user.id}, {'password':new_password, 'auth':auth}) 152 | raise web.SeeOther('/settings') 153 | else: 154 | return render.password('原密码不正确', self.crumb.output(), self.form) 155 | else: 156 | return render.password('修改密码', self.crumb.output(), self.form) 157 | 158 | 159 | class profile: 160 | 161 | def GET(self, name): 162 | limit = 10 163 | user = user_model().get_one({'name':name}) 164 | if user is None: 165 | crumb = Crumb() 166 | crumb.append('会员未找到') 167 | return render.user_nf('会员未找到', crumb.output()) 168 | else: 169 | posts_result = post_model().get_all({'user_id':user.id}, limit = limit, order = 'time DESC') 170 | if len(posts_result) > 0: 171 | posts = [] 172 | for post_result in posts_result: 173 | post = {'post':post_result} 174 | node = node_model().get_one({'id':post_result.node_id}) 175 | post['node'] = node 176 | comment = comment_model().get_one({'post_id':post_result.id}, order='time DESC') 177 | if comment: 178 | comment_user = user_model().get_one({'id':comment.user_id}) 179 | post['comment_user'] = comment_user 180 | else: 181 | post['comment_user'] = None 182 | posts.append(post) 183 | else: 184 | posts = None 185 | comments_result = comment_model().get_all({'user_id':user.id}, limit = limit, order = 'time DESC') 186 | if len(comments_result) > 0: 187 | comments = [] 188 | for comment_result in comments_result: 189 | post = post_model().get_one({'id':comment_result.post_id}) 190 | post_user = user_model().get_one({'id':post.user_id}) 191 | comment = {'post':post, 'comment':comment_result, 'post_user':post_user} 192 | comments.append(comment) 193 | else: 194 | comments = None 195 | following = False 196 | if session.user_id: 197 | if user_meta_model().get_one({'user_id':session.user_id, 'meta_key':'follow', 'meta_value':user.id}): 198 | following = True 199 | return render.profile(user.name, user, posts, comments, following) 200 | 201 | class avatar: 202 | 203 | def __init__(self): 204 | if session.user_id is None: 205 | raise web.SeeOther('/login?next=/settings/avatar') 206 | self.crumb = Crumb() 207 | self.user = user_model().get_one({'id':session.user_id}) 208 | 209 | def GET(self): 210 | self.crumb.append('设置', '/settings') 211 | self.crumb.append('上传头像') 212 | return render.avatar('上传头像', self.user, self.crumb.output()) 213 | def POST(self): 214 | import cgi 215 | import os 216 | cgi.maxlen = 2 * 1024 * 1024 # 2MB 217 | try: 218 | x = web.input(avatar={}) 219 | except ValueError: 220 | return render.avatar('上传头像', self.user, self.crumb.output(), ' <<超过大小限制') 221 | if 'avatar' in x: 222 | #客户端为windows时注意 223 | filepath=x.avatar.filename.replace('\\','/') 224 | #获取文件名 225 | filename=filepath.split('/')[-1] 226 | #获取后缀 227 | ext = filename.split('.', 1)[1].lower() 228 | ext_allow = ('jpg', 'png', 'gif', 'jpeg') 229 | #判断文件后缀名 230 | if ext in ext_allow: 231 | #要上传的路径 232 | filedir = 'static/avatar/tmp/' 233 | try: 234 | os.makedirs('static/avatar/tmp') 235 | except: 236 | pass 237 | filename = str(session.user_id) +'.'+ext 238 | if os.path.exists(filedir+filename): 239 | os.remove(filedir+filename) 240 | fout = open(filedir + filename, 'wb') 241 | fout.write(x.avatar.file.read()) 242 | fout.close() 243 | user_model().set_avatar(filename, self.user.id) 244 | error = False 245 | else: 246 | message = ' <<请上传指定格式文件' 247 | error = True 248 | if error: 249 | return render.avatar('上传头像', self.user, self.crumb.output(), message) 250 | else: 251 | raise web.SeeOther('/settings/avatar') 252 | 253 | # 收藏的主题 254 | class post_favs(): 255 | 256 | def __init__(self): 257 | if session.user_id is None: 258 | raise web.SeeOther('/login?next=/my/posts') 259 | self.crumb = Crumb() 260 | 261 | def GET(self): 262 | limit = 10 263 | self.crumb.append('我收藏的主题') 264 | user = user_model().get_one({'id':session.user_id}) 265 | pagination = Pagination('/my/posts', user.post_favs, limit = limit) 266 | if user.post_favs > 0: 267 | page = pagination.true_page(web.input(p=1)['p']) 268 | post_favs = user_meta_model().get_all({'user_id':user.id, 'meta_key':'post_fav'}, limit = limit, offset = (page-1)*limit, order = 'id DESC') 269 | posts = [] 270 | for post_fav in post_favs: 271 | post_result = post_model().get_one({'id':post_fav.meta_value}) 272 | post = {'post':post_result} 273 | user = user_model().get_one({'id':post_result.user_id}) 274 | post['user'] = user 275 | node = node_model().get_one({'id':post_result.node_id}) 276 | post['node'] = node 277 | comment = comment_model().get_one({'post_id':post_result.id}, order='time DESC') 278 | if comment: 279 | comment_user = user_model().get_one({'id':comment.user_id}) 280 | post['comment_user'] = comment_user 281 | else: 282 | post['comment_user'] = None 283 | posts.append(post) 284 | else: 285 | posts = None 286 | return render.post_favs('我收藏的主题', user, posts, self.crumb.output(), pagination.output()) 287 | 288 | # 来自收藏节点的主题 289 | class node_favs: 290 | 291 | def __init__(self): 292 | if session.user_id is None: 293 | raise web.SeeOther('/login?next=/my/nodes') 294 | self.crumb = Crumb() 295 | 296 | def GET(self): 297 | limit = 10 298 | self.crumb.append('来自我收藏的节点的最新主题') 299 | # 取出收藏的节点id 300 | node_favs = user_meta_model().get_all({'user_id':session.user_id, 'meta_key':'node_fav'}) 301 | if len(node_favs) > 0 : 302 | nodes = [] 303 | for node_fav in node_favs: 304 | nodes.append(node_fav.meta_value) 305 | total_rows = post_model().count_table({'node_id':nodes}) 306 | pagination = Pagination('/my/nodes', total_rows, limit = limit) 307 | page = pagination.true_page(web.input(p=1)['p']) 308 | posts_result = post_model().get_all(conditions = {'node_id': nodes}, order = 'time DESC', limit = limit, offset = (page-1)*limit) 309 | posts = [] 310 | for post_result in posts_result: 311 | post = {'post':post_result} 312 | user = user_model().get_one({'id':post_result.user_id}) 313 | post['user'] = user 314 | node = node_model().get_one({'id':post_result.node_id}) 315 | post['node'] = node 316 | comment = comment_model().get_one({'post_id':post_result.id}, order='time DESC') 317 | if comment: 318 | comment_user = user_model().get_one({'id':comment.user_id}) 319 | post['comment_user'] = comment_user 320 | else: 321 | post['comment_user'] = None 322 | posts.append(post) 323 | else: 324 | posts = None 325 | total_rows = 0 326 | pagination = Pagination('/my/nodes', total_rows) 327 | page = pagination.true_page(web.input(p=1)['p']) 328 | return render.node_favs('来自我收藏的节点的最新主题', posts, total_rows, self.crumb.output(), pagination.output()) 329 | 330 | # 用户创建的主题 331 | class posts: 332 | 333 | def GET(self, name): 334 | limit = 10 335 | user = user_model().get_one({'name':name}) 336 | crumb = Crumb() 337 | if user: 338 | crumb.append(name, '/profile/'+name) 339 | crumb.append('全部主题') 340 | total_rows = post_model().count_table({'user_id':user.id}) 341 | pagination = Pagination('/profile/'+name+'/posts', total_rows, limit = limit) 342 | page = pagination.true_page(web.input(p=1)['p']) 343 | posts_result = post_model().get_all({'user_id':user.id}, limit = limit, offset = (page-1) * limit, order = 'time DESC') 344 | posts = [] 345 | for post_result in posts_result: 346 | post = {'post':post_result} 347 | node = node_model().get_one({'id':post_result.node_id}) 348 | post['node'] = node 349 | comment = comment_model().get_one({'post_id':post_result.id}, order='time DESC') 350 | if comment: 351 | comment_user = user_model().get_one({'id':comment.user_id}) 352 | post['comment_user'] = comment_user 353 | else: 354 | post['comment_user'] = None 355 | posts.append(post) 356 | return render.user_posts('全部主题', user, posts, total_rows, crumb.output(), pagination.output()) 357 | else: 358 | crumb.append('会员未找到') 359 | return render.user_nf('会员未找到', crumb.output()) 360 | 361 | # 用户创建的回复 362 | class comments: 363 | 364 | def GET(self, name): 365 | limit = 10 366 | user = user_model().get_one({'name':name}) 367 | crumb = Crumb() 368 | if user: 369 | crumb.append(name, '/profile/'+name) 370 | crumb.append('全部回复') 371 | total = comment_model().count_table({'user_id':user.id}) 372 | pagination = Pagination('/profile/'+name+'/comments', total, limit = limit) 373 | page = pagination.true_page(web.input(p=1)['p']) 374 | comments_result = comment_model().get_all({'user_id':user.id}, limit = limit, offset = (page-1)*limit, order = 'time DESC') 375 | if len(comments_result) > 0: 376 | comments = [] 377 | for comment_result in comments_result: 378 | post = post_model().get_one({'id':comment_result.post_id}) 379 | post_user = user_model().get_one({'id':post.user_id}) 380 | comment = {'post':post, 'comment':comment_result, 'post_user':post_user} 381 | comments.append(comment) 382 | else: 383 | comments = None 384 | return render.user_comments('全部回复', comments, total, crumb.output(), pagination.output()) 385 | else: 386 | crumb.append('会员未找到') 387 | return render.user_nf('会员未找到', crumb.output()) 388 | 389 | # 关注用户 390 | class follow: 391 | 392 | def GET(self, name): 393 | user = user_model().get_one({'name':name}) 394 | if user is None: 395 | crumb = Crumb() 396 | crumb.append('会员未找到') 397 | return render.user_nf('会员未找到', crumb.output()) 398 | else: 399 | if session.user_id is None: 400 | raise web.SeeOther('/login?next=/profile/'+name) 401 | user_meta_model().unique_insert({'user_id':session.user_id, 'meta_key':'follow', 'meta_value':user.id}) 402 | user_model().update({'id':session.user_id}, {'user_favs':user_meta_model().count_meta({'user_id':session.user_id, 'meta_key':'follow'})}) 403 | user_model().update_session(session.user_id) 404 | raise web.SeeOther('/profile/'+name) 405 | 406 | # 取消关注用户 407 | class unfollow: 408 | 409 | def GET(self, name): 410 | user = user_model().get_one({'name':name}) 411 | if user is None: 412 | crumb = Crumb() 413 | crumb.append('会员未找到') 414 | return render.user_nf('会员未找到', crumb.output()) 415 | else: 416 | if session.user_id is None: 417 | raise web.SeeOther('/login?next=/profile/'+name) 418 | user_meta_model().delete({'user_id':session.user_id, 'meta_key':'follow', 'meta_value':user.id}) 419 | user_model().update({'id':session.user_id}, {'user_favs':user_meta_model().count_meta({'user_id':session.user_id, 'meta_key':'follow'})}) 420 | user_model().update_session(session.user_id) 421 | raise web.SeeOther('/profile/'+name) 422 | 423 | # 来自关注用户的帖子 424 | class following: 425 | 426 | def __init__(self): 427 | if session.user_id is None: 428 | raise web.SeeOther('/login?next=/user/nodes') 429 | self.crumb = Crumb() 430 | 431 | def GET(self): 432 | limit = 10 433 | self.crumb.append('我关注的人的最新主题') 434 | # 取出收藏的节点id 435 | followings = user_meta_model().get_all({'user_id':session.user_id, 'meta_key':'follow'}) 436 | if len(followings) > 0 : 437 | user_favs = [] 438 | for following in followings: 439 | user_favs.append(following.meta_value) 440 | total_rows = post_model().count_table({'user_id':user_favs}) 441 | pagination = Pagination('/my/following', total_rows, limit = limit) 442 | page = pagination.true_page(web.input(p=1)['p']) 443 | posts_result = post_model().get_all(conditions = {'user_id': user_favs}, order = 'time DESC', limit = limit, offset = (page-1)*limit) 444 | posts = [] 445 | for post_result in posts_result: 446 | post = {'post':post_result} 447 | user = user_model().get_one({'id':post_result.user_id}) 448 | post['user'] = user 449 | node = node_model().get_one({'id':post_result.node_id}) 450 | post['node'] = node 451 | comment = comment_model().get_one({'post_id':post_result.id}, order='time DESC') 452 | if comment: 453 | comment_user = user_model().get_one({'id':comment.user_id}) 454 | post['comment_user'] = comment_user 455 | else: 456 | post['comment_user'] = None 457 | posts.append(post) 458 | else: 459 | posts = None 460 | total_rows = 0 461 | pagination = Pagination('/my/nodes', total_rows) 462 | page = pagination.true_page(web.input(p=1)['p']) 463 | return render.following_posts('来自我收藏的节点的最新主题', posts, total_rows, self.crumb.output(), pagination.output()) 464 | 465 | class balance: 466 | 467 | def __init__(self): 468 | if session.user_id is None: 469 | raise web.SeeOther('/login?next=/balance') 470 | self.crumb = Crumb() 471 | 472 | def GET(self): 473 | limit = 20 474 | total = money_model().count_table({'user_id':session.user_id}) 475 | pagination = Pagination('/balance', total, limit = limit) 476 | page = pagination.true_page(web.input(p=1)['p']) 477 | records_result = money_model().get_all({'user_id':session.user_id}, limit = limit, offset = (page-1)*limit, order = 'id DESC') 478 | money_types_result = money_type_model().get_all() 479 | money_type = {} 480 | for money_type_result in money_types_result: 481 | money_type[money_type_result.id] = money_type_result.name 482 | records = [] 483 | for record_result in records_result: 484 | # 发布的帖子或者是评论的帖子 485 | post = None 486 | # 发布或者收到的评论 487 | post_user = None 488 | post_thanks = None 489 | comment_thanks = None 490 | sender = None 491 | comment = None 492 | # 评论的用户 493 | comment_user = None 494 | try: 495 | type = money_type[record_result.money_type_id] 496 | if type == 'post': 497 | post = post_model().get_one({'id':record_result.foreign_id}) 498 | if type == 'comment': 499 | comment = comment_model().get_one({'id':record_result.foreign_id}) 500 | comment_user = user_model().get_one({'id':comment.user_id}) 501 | post = post_model().get_one({'id':comment.post_id}) 502 | if type == 'post_thanks': 503 | post_thanks = post_thanks_model().get_one({'id':record_result.foreign_id}) 504 | post = post_model().get_one({'id':post_thanks.post_id}) 505 | sender = user_model().get_one({'id':post_thanks.user_id}) 506 | post_user = user_model().get_one({'id':post.user_id}) 507 | if type == 'comment_thanks': 508 | comment_thanks = comment_thanks_model().get_one({'id':record_result.foreign_id}) 509 | comment = comment_model().get_one({'id':comment_thanks.comment_id}) 510 | post = post_model().get_one({'id':comment.post_id}) 511 | comment_user = user_model().get_one({'id':comment.user_id}) 512 | sender = user_model().get_one({'id':comment_thanks.user_id}) 513 | # 如果数据错误将不把这条记录输出到视图 514 | except AttributeError: 515 | continue 516 | else: 517 | record = {'record':record_result, 'type':type, 'comment':comment, 'post':post, 'post_user':post_user, 'sender':sender, 'comment_user':comment_user, 'post_thanks':post_thanks, 'comment_thanks':comment_thanks} 518 | records.append(record) 519 | self.crumb.append('账户余额') 520 | return render.money_record('账户余额', records, self.crumb.output(), pagination.output()) --------------------------------------------------------------------------------