6 import tornado.httpserver
9 from tornado.options import define,options
10 from tinydb import TinyDB,Query,where
11 from tinydb.operations import delete
12 from datetime import datetime,date
15 define('port',default=8000,help='run on the given port',type=int)
17 class BaseHandler(tornado.web.RequestHandler):
18 def get_current_user(self):
19 user = self.get_secure_cookie('admin_user')
20 return tornado.escape.utf8(user)
22 def set_current_user(self,username):
23 self.set_secure_cookie('admin_user',username)
25 def clear_current_user(self):
26 self.clear_cookie('admin_user')
28 class IndexHandler(BaseHandler):
29 def get(self,dbname,page='0'):
30 params = self.application.db.get(where('kinds') == 'conf')
31 if params['mentenance'] == True:
32 self.render('mentenance.htm',title=params['title'],db=dbname)
34 if self.application.collection(dbname) == False:
35 if self.current_user == b'admin':
36 self.application.db.table(dbname)
38 raise tornado.web.HTTPError(404)
40 key = self.get_argument('key','')
42 table = self.application.db.table(dbname)
43 rec = table.get(where('number') == int(key))
45 self.render('article.htm',record=rec)
48 raise tornado.web.HTTPError(404)
51 rule = tornado.escape.url_unescape(self.get_cookie('aikotoba',''))
52 na = tornado.escape.url_unescape(self.get_cookie("username",u"誰かさん"))
53 pos = self.application.gpos(dbname,page)
54 table = self.application.db.table(dbname)
60 bool = (dbname == params['info name'])
61 rec = sorted(table.all(),key=lambda x: x['number'])[start:start+i]
64 if len(table) >= 10*i:
65 self.render('modules/full.htm',position=pos,records=rec,data=params,db=dbname)
67 if (bool == True)and(self.current_user != b'admin'):
68 self.render('modules/info.htm',position=pos,records=rec,data=params,db=dbname)
70 self.render('modules/index.htm',position=pos,records=rec,data=params,username=na,db=dbname,aikotoba=rule)
72 class LoginHandler(BaseHandler):
74 query = self.get_query_argument('next','')
75 i = query[1:].find('/')
80 self.render('login.htm',db=qs)
83 pw = self.application.db.get(where('kinds') == 'conf')
84 if self.get_argument('password') == pw['password']:
85 self.set_current_user('admin')
86 dbname = self.get_argument('record')
87 if dbname == 'master':
88 self.redirect('/master')
90 self.redirect('/'+dbname+'/admin/0/')
92 class LogoutHandler(BaseHandler):
94 self.clear_current_user()
95 self.redirect('/login')
97 class NaviHandler(tornado.web.RequestHandler):
100 if self.application.collection('params') == False:
101 item = {"mentenance":False,"out_words":[u"阿保",u"馬鹿",u"死ね"],"password":"admin",
102 "title2":"<h1 style=color:gray;text-align:center>pybbs</h1>",
103 "bad_words":["<style","<link","<script","<img"],"count":30,
104 "title":"pybbs","info name":"info"}
105 self.application.db.insert(item)
106 self.render('top.htm',coll=col,name=na,full=self.full)
109 names = self.application.db.tables()
110 na = self.application.db.get(where('kinds') == 'conf')['info name']
111 for s in ['_default','master','temp']:
118 return sorted(names),na
120 def full(self,dbname):
121 if dbname in self.application.db.tables():
122 i = 10*self.application.db.get(where('kinds') == 'conf')['count']
123 table = self.application.db.table(dbname)
128 class TitleHandler(NaviHandler):
130 rec = sorted(self.title(),key=lambda x: x['date2'])
131 self.render('title.htm',coll=rec,full=self.full)
134 names = self.application.db.tables()
135 for s in ['_default','master','temp']:
141 table = self.application.db.table(x)
144 if table.contains(where('number') == 1) == True:
145 s = table.get(where('number') == 1)['title']
153 rec = sorted(table.all(),key=lambda k: k['number'])
156 i = datetime.strptime(s,'%Y/%m/%d %H:%M')
157 year = datetime.now().year-i.year
164 item['date2'] = j+31*(i.month-1)+i.day
167 class RegistHandler(tornado.web.RequestHandler):
168 def post(self,dbname):
169 if self.application.collection(dbname) == False:
170 raise tornado.web.HTTPError(404)
172 self.database = dbname
173 rec = self.application.db.get(where('kinds') == 'conf')
174 words = rec['bad_words']
175 out = rec['out_words']
176 na = self.get_argument('name')
177 sub = self.get_argument('title')
178 com = self.get_argument('comment',None,False)
185 error = error + u'禁止ワード.'
187 for line in com.splitlines(True):
191 if word in line.lower():
192 error = error + u'タグ違反.('+word+')'
194 obj = re.finditer('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', line)
196 if x.group() not in url:
197 url.append(x.group())
198 if re.match(' ',line):
199 line = line.replace(' ',' ',1)
200 text = text+'<p>'+self.link(line)+'<br></p>'
203 s = s+'<tr><td><a class=livepreview target=_blank href={0}>{0}</a></td></tr>'.format(x)
205 text = text+'<table><tr><td>検出URL:</td></tr>'+s+'</table>'
206 pw = self.get_argument('password')
208 error = error + u'本文がありません.'
210 error = error +u'文字数が1,000をこえました.'
211 article = self.application.db.table(dbname)
212 if len(article) == 0:
215 item = sorted(article.all(),key=lambda x: x['number'])[len(article)-1]
216 no = item['number']+1
223 reg = {'number':no,'name':na,'title':sub,'comment':text,'raw':com,'password':pw,'date':s.strftime('%Y/%m/%d %H:%M')}
225 self.set_cookie('username',tornado.escape.url_escape(na))
226 self.redirect('/'+dbname+'#article')
228 self.render('regist.htm',content=error)
230 def link(self,command):
233 obj = re.finditer('>>[0-9]+',command)
235 s = '<a class=minpreview data-preview-url=/{0}?key={1} href=/{0}/userdel?job={1}>>>{1}</a>'.format(self.database,x.group()[2:])
236 text = text+command[i:x.start()]+s
239 text = text+command[i:]
242 class AdminHandler(BaseHandler):
243 @tornado.web.authenticated
244 def get(self,dbname,page):
246 dbname = self.get_argument('record','')
247 if self.application.collection(dbname) == False:
248 raise tornado.web.HTTPError(404)
250 table = self.application.db.table(dbname)
251 rec = sorted(table.all(),key=lambda x: x['number'])
252 mente = self.application.db.get(where('kinds') == 'conf')
253 if mente['mentenance'] == True:
254 check = 'checked=checked'
257 pos = self.application.gpos(dbname,page)
264 self.render('modules/admin.htm',position=pos,records=rec[start:start+i],mente=check,password=mente['password'],db=dbname)
266 class AdminConfHandler(BaseHandler):
267 @tornado.web.authenticated
268 def post(self,dbname,func):
270 param = self.application.db.get(where('kinds') == 'conf')['mentenance']
271 if self.get_argument('mente','') == 'on':
279 word = self.get_argument('pass','')
281 self.render('regist.htm',content='パスワードを設定してください')
284 self.application.db.update({'mentenance':mente,'password':word},where('kinds') == 'conf')
286 table = self.application.db.table(dbname)
287 for x in self.get_arguments('item'):
288 table.remove(where('number') == int(x))
289 self.redirect('/'+dbname+'/admin/0/')
292 self.application.db.close()
293 shutil.copy(st.json,st.bak)
294 self.application.db = TinyDB(st.json)
297 database = self.application.db
299 for x in database.tables():
300 if self.application.collection(x) == True:
301 database.purge_table(x)
302 if x in bak.tables():
303 table = database.table(x)
304 table.insert_multiple(bak.table(x).all())
306 class UserHandler(tornado.web.RequestHandler):
308 def get(self,dbname):
309 self.table = self.application.db.table(dbname)
310 q = self.get_query_argument('job','0',strip=True)
311 num = self.page(int(q))
313 self.redirect('/{0}#{1}'.format(dbname,q))
315 self.redirect('/{0}{1}#{2}'.format(dbname,num,q))
317 def post(self,dbname):
318 number = self.get_argument('number')
319 if number.isdigit() == True:
321 pas = self.get_argument('password')
322 self.table = self.application.db.table(dbname)
324 obj = self.table.get(qwr.number == num)
325 if obj and(obj['password'] == pas):
326 self.table.update({'title':u'削除されました','name':'','comment':u'<i><b>投稿者により削除されました</b></i>'},qwr.number == num)
327 self.redirect('/{0}{1}#{2}'.format(dbname,self.page(num),number))
329 self.redirect('/'+dbname)
331 def page(self,number):
332 if self.table != None:
333 rec = self.table.count(where('number') <= number)
334 conf = self.application.db.get(where('kinds') == 'conf')
335 if len(self.table)-rec >= conf['count']:
336 return '/'+str(1+rec//conf['count'])+'/'
340 class SearchHandler(tornado.web.RequestHandler):
341 def post(self,dbname):
342 arg = self.get_argument('word1')
344 self.radiobox = self.get_argument('filter')
345 rec = sorted(self.search(dbname),key=lambda x: x['number'])
346 self.render('modules/search.htm',records=rec,word1=arg,db=dbname)
348 def get(self,dbname):
349 if self.application.collection(dbname) == False:
350 raise tornado.web.HTTPError(404)
352 self.render('modules/search.htm',records=[],word1='',db=dbname)
354 def search(self,dbname):
355 table = self.application.db.table(dbname)
356 element = self.word.split()
357 if len(element) == 0:
359 while len(element) < 3:
360 element.append(element[0])
361 if self.radiobox == 'comment':
362 query = (Query().raw.search(element[0])) | (Query().raw.search(element[1])) | (Query().raw.search(element[2]))
364 query = (Query().name == element[0]) | (Query().name == element[1]) | (Query().name == element[2])
365 if self.radiobox == 'comment':
366 for x in table.search(query):
368 for text in x['raw'].splitlines(True):
370 if text.find(word) > -1:
371 com = com +'<p style=background-color:yellow>'+text+'<br></p>'
374 com = com+'<p>'+text+'<br></p>'
378 for x in table.search(query):
381 class FooterModule(tornado.web.UIModule):
382 def render(self,number,url,link):
383 return self.render_string('modules/footer.htm',index=number,url=url,link=link)
385 class HeadlineApi(tornado.web.RequestHandler):
388 for x in self.application.db.tables():
390 response[x] = self.get_data(x)
391 self.write(json.dumps(response,ensure_ascii=False))
393 def get_data(self,dbname):
394 table = self.application.db.table(dbname)
399 rec = sorted(table.all(),key=lambda x: x['number'])[i-1]
400 return {'number':rec['number'],'title':rec['title'],'name':rec['name'],'comment':rec['raw'][0:19]}
402 class ArticleApi(tornado.web.RequestHandler):
403 def get(self,dbname,number):
404 if self.application.collection(dbname) == True:
405 table = self.application.db.table(dbname)
406 response = table.get(where('number') == int(number))
410 del response['comment']
411 self.write(json.dumps(response,ensure_ascii=False))
413 tornado.web.HTTPError(404)
415 def post(self,dbname):
416 name = self.get_argument('name',u'誰かさん')
417 title = self.get_argument('title',u'タイトルなし')
418 comment = self.get_argument('comment')
419 table = self.application.db.table(dbname)
420 table.insert({'name':name,'title':title,'comment':comment})
422 class HelpHandler(tornado.web.RequestHandler):
424 self.render('help.htm',req='')
427 com = self.get_argument('help','')
429 for line in com.splitlines():
431 table = self.application.db.table('master')
432 time = datetime.now()
433 table.insert({'comment':text,'time':time.strftime('%Y/%m/%d %H:%M')})
438 self.render('help.htm',req=req)
440 class MasterHandler(BaseHandler):
441 @tornado.web.authenticated
443 if self.current_user == b'admin':
444 com = self.application.db.table('master').all()
445 self.render('master.htm',com=com)
447 raise tornado.web.HTTPError(404)
449 class AlertHandler(UserHandler):
451 db = self.get_query_argument('db')
452 num = self.get_query_argument('num')
453 self.table = self.application.db.table(db)
454 tb = self.table.get(where('number') == int(num))
455 s = self.page(int(num))
456 jump = '/'+db+s+'#'+num
457 link = '<p><a href={0}>{0}</a>'.format(jump)
458 time = datetime.now()
459 data = {'comment':tb['comment']+link,'time':time.strftime('%Y/%m/%d'),
460 'link':jump,'date':date.weekday(time)}
461 id = self.application.db.table('temp').insert(data)
462 self.render('alert.htm',com=data['comment'],num=id)
465 id = int(self.get_argument('num'))
466 table = self.application.db.table('temp')
467 tb = table.get(eid=id)
469 table.remove(eids=[id])
470 table.remove(where('date') != date.weekday(datetime.now()))
471 if self.get_argument('admit','') == 'ok':
472 com = self.get_argument('com')
473 tb['comment'] = com+tb['comment']
475 table = self.application.db.table('master')
479 class Application(tornado.web.Application):
481 self.db = TinyDB(st.json)
482 handlers = [(r'/',NaviHandler),(r'/login',LoginHandler),(r'/logout',LogoutHandler),(r'/title',TitleHandler),
483 (r'/headline/api',HeadlineApi),(r'/read/api/([a-zA-Z0-9_]+)/([0-9]+)',ArticleApi),(r'/write/api/([a-zA-Z0-9_]+)',ArticleApi),
484 (r'/help',HelpHandler),(r'/master/*',MasterHandler),(r'/alert',AlertHandler),
485 (r'/([a-zA-Z0-9_]+)',IndexHandler),(r'/([a-zA-Z0-9_]+)/([0-9]+)/*',IndexHandler),
486 (r'/([a-zA-Z0-9_]+)/admin/([0-9]+)/*',AdminHandler),(r'/([a-zA-Z0-9_]+)/admin/([a-z]+)/*',AdminConfHandler),(r'/([a-zA-Z0-9_]+)/userdel',UserHandler),
487 (r'/([a-zA-Z0-9_]+)/search',SearchHandler),(r'/([a-zA-Z0-9_]+)/regist',RegistHandler)]
488 settings = {'template_path':os.path.join(os.path.dirname(__file__),'templates'),
489 'static_path':os.path.join(os.path.dirname(__file__),'static'),
490 'ui_modules':{'Footer':FooterModule},
491 'cookie_secret':'bZJc2sWbQLKo6GkHn/VB9oXwQt8SOROkRvJ5/xJ89Eo=',
496 tornado.web.Application.__init__(self,handlers,**settings)
498 def gpos(self,dbname,page):
499 params = self.db.get(where('kinds') == 'conf')
503 elif (pos-1)*params['count'] >= len(self.db.table(dbname)):
507 def collection(self,name):
508 if name in self.db.tables():
514 json = 'static/db/db.json'
515 bak = 'static/db/bak.json'
518 if __name__ == '__main__':
519 tornado.options.parse_command_line()
520 http_server = tornado.httpserver.HTTPServer(Application())
521 http_server.listen(options.port)
522 tornado.ioloop.IOLoop.instance().start()