OSDN Git Service

smart phone
[pybbs/pybbs.git] / index.py
1
2 import os.path
3 import shutil,re
4 import tornado.escape
5 import tornado.web
6 import tornado.httpserver
7 import tornado.ioloop
8 import tornado.options
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
13 import json
14
15 define('port',default=8000,help='run on the given port',type=int)
16
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)
21     
22     def set_current_user(self,username):
23         self.set_secure_cookie('admin_user',username)
24         
25     def clear_current_user(self):
26         self.clear_cookie('admin_user')
27
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)
33             return
34         if self.application.collection(dbname) == False:
35             if self.current_user == b'admin':
36                 self.application.db.table(dbname)
37             else:
38                 raise tornado.web.HTTPError(404)
39                 return
40         key = self.get_argument('key','')
41         if key:
42             table = self.application.db.table(dbname)
43             rec = table.get(where('number') == int(key))
44             if rec:
45                 self.render('article.htm',record=rec)
46                 return
47             else:
48                 raise tornado.web.HTTPError(404)
49                 return
50         i = params['count']      
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)
55         start = (pos-1)*i
56         if start < 0:
57             start = len(table)-i
58             if start < 0:
59                 start = 0
60         bool = (dbname == params['info name'])
61         rec = sorted(table.all(),key=lambda x: x['number'])[start:start+i]
62         if bool == True:
63             rec = rec[::-1]
64         if len(table) >= 10*i:
65             self.render('modules/full.htm',position=pos,records=rec,data=params,db=dbname)
66             return
67         if (bool == True)and(self.current_user != b'admin'):
68             self.render('modules/info.htm',position=pos,records=rec,data=params,db=dbname)
69         else:
70             self.render('modules/index.htm',position=pos,records=rec,data=params,username=na,db=dbname,aikotoba=rule)
71         
72 class LoginHandler(BaseHandler):
73     def get(self):
74         query = self.get_query_argument('next','')
75         i = query[1:].find('/')
76         if i == -1:
77             qs = query[1:]
78         else:
79             qs = query[1:i+1]  
80         self.render('login.htm',db=qs)
81         
82     def post(self):
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')
89         else:
90             self.redirect('/'+dbname+'/admin/0/')
91         
92 class LogoutHandler(BaseHandler):
93     def get(self):
94         self.clear_current_user()
95         self.redirect('/login')
96         
97 class NaviHandler(tornado.web.RequestHandler):              
98     def get(self):
99         col,na = self.name()
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)
107         
108     def name(self):
109         names = self.application.db.tables()
110         na = self.application.db.get(where('kinds') == 'conf')['info name']
111         for s in ['_default','master','temp']:
112             if s in names:
113                 names.remove(s)
114         if na in names:
115             names.remove(na)
116         else:
117             na = ''
118         return sorted(names),na
119                 
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)
124             if len(table) >= i:
125                 return True
126         return False
127
128 class TitleHandler(NaviHandler):
129     def get(self):
130         rec = sorted(self.title(),key=lambda x: x['date2'])
131         self.render('title.htm',coll=rec,full=self.full)  
132         
133     def title(self):
134         names = self.application.db.tables()
135         for s in ['_default','master','temp']:
136             if s in names:
137                 names.remove(s)
138         for x in names:
139             item = {}
140             item['name'] = x
141             table = self.application.db.table(x)
142             i = len(table)
143             item['count'] = i            
144             if table.contains(where('number') == 1) == True:
145                 s = table.get(where('number') == 1)['title']
146             else:
147                 s = ''
148             item['title'] = s   
149             if i == 0:
150                 item['date'] = ''
151                 item['date2'] = 0
152             else:
153                 rec = sorted(table.all(),key=lambda k: k['number'])
154                 s = rec[i-1]['date']
155                 item['date'] = s
156                 i = datetime.strptime(s,'%Y/%m/%d %H:%M')
157                 year = datetime.now().year-i.year
158                 if year == 0:
159                     j = 800
160                 elif year == 1:
161                     j = 400
162                 else:
163                     j = 0
164                 item['date2'] = j+31*(i.month-1)+i.day
165             yield item
166         
167 class RegistHandler(tornado.web.RequestHandler):
168     def post(self,dbname):
169         if self.application.collection(dbname) == False:
170             raise tornado.web.HTTPError(404)
171             return
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)
179         text = ''
180         i = 0
181         url = []
182         error = ''
183         for word in out:
184             if word in com:
185                 error = error + u'禁止ワード.'
186                 break
187         for line in com.splitlines(True):
188             if error != '':
189                 break
190             for word in words:
191                 if word in line.lower():
192                     error = error + u'タグ違反.('+word+')'       
193             i += len(line)   
194             obj = re.finditer('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', line)
195             for x in obj:
196                 if x.group() not in url:
197                     url.append(x.group())
198             if re.match(' ',line):
199                 line = line.replace(' ','&nbsp;',1)
200             text = text+'<p>'+self.link(line)+'<br></p>'
201         s = ''
202         for x in url:
203             s = s+'<tr><td><a class=livepreview target=_blank href={0}>{0}</a></td></tr>'.format(x)
204         if s:
205             text = text+'<table><tr><td>検出URL:</td></tr>'+s+'</table>'
206         pw = self.get_argument('password')
207         if i == 0:
208             error = error + u'本文がありません.'
209         elif i > 1000:
210             error = error +u'文字数が1,000をこえました.'
211         article = self.application.db.table(dbname)
212         if len(article) == 0:
213             no = 1
214         else:
215             item = sorted(article.all(),key=lambda x: x['number'])[len(article)-1]
216             no = item['number']+1
217         if error == '':
218             if not na:
219                 na = u'誰かさん'
220             if sub == '':
221                 sub = u'タイトルなし.'
222             s = datetime.now()
223             reg = {'number':no,'name':na,'title':sub,'comment':text,'raw':com,'password':pw,'date':s.strftime('%Y/%m/%d %H:%M')}
224             article.insert(reg)
225             self.set_cookie('username',tornado.escape.url_escape(na))
226             self.redirect('/'+dbname+'#article')
227         else:
228             self.render('regist.htm',content=error)
229     
230     def link(self,command):
231         i = 0
232         text = ''
233         obj = re.finditer('>>[0-9]+',command)
234         for x in obj:
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
237             i = x.end()
238         else:
239             text = text+command[i:]
240         return text
241     
242 class AdminHandler(BaseHandler):
243     @tornado.web.authenticated               
244     def get(self,dbname,page):
245         if dbname == '':
246             dbname = self.get_argument('record','')
247         if self.application.collection(dbname) == False:
248             raise tornado.web.HTTPError(404)
249             return
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'
255         else:
256             check = ''
257         pos = self.application.gpos(dbname,page)
258         i = mente['count']
259         start = (pos-1)*i
260         if start < 0:
261             start = len(table)-i
262             if start < 0:
263                 start = 0
264         self.render('modules/admin.htm',position=pos,records=rec[start:start+i],mente=check,password=mente['password'],db=dbname)
265
266 class AdminConfHandler(BaseHandler):
267     @tornado.web.authenticated
268     def post(self,dbname,func):
269         if func == 'set':
270             param = self.application.db.get(where('kinds') == 'conf')['mentenance']
271             if self.get_argument('mente','') == 'on':
272                 mente = True
273                 if param != mente:
274                     self.store()
275             else:
276                 mente = False  
277                 if param != mente:
278                     self.restore()
279             word = self.get_argument('pass','')
280             if word == '':
281                 self.render('regist.htm',content='パスワードを設定してください')
282                 return
283             else:
284                 self.application.db.update({'mentenance':mente,'password':word},where('kinds') == 'conf')  
285         elif func == 'del':
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/')
290         
291     def store(self):
292         self.application.db.close()
293         shutil.copy(st.json,st.bak)
294         self.application.db = TinyDB(st.json)
295         
296     def restore(self):
297         database = self.application.db
298         bak = TinyDB(st.bak)
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())
305           
306 class UserHandler(tornado.web.RequestHandler):
307     table = None
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))        
312         if num == '':
313             self.redirect('/{0}#{1}'.format(dbname,q))           
314         else:
315             self.redirect('/{0}{1}#{2}'.format(dbname,num,q))
316         
317     def post(self,dbname):
318         number = self.get_argument('number')
319         if number.isdigit() == True:
320             num = int(number)
321             pas = self.get_argument('password')
322             self.table = self.application.db.table(dbname)
323             qwr = Query()
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))
328             else:
329                 self.redirect('/'+dbname)
330                 
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'])+'/'
337             else:
338                 return ''
339       
340 class SearchHandler(tornado.web.RequestHandler):       
341     def post(self,dbname):
342         arg = self.get_argument('word1')
343         self.word = arg 
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)
347     
348     def get(self,dbname):
349         if self.application.collection(dbname) == False:
350             raise tornado.web.HTTPError(404)
351             return
352         self.render('modules/search.htm',records=[],word1='',db=dbname)
353         
354     def search(self,dbname):
355         table = self.application.db.table(dbname)    
356         element = self.word.split()
357         if len(element) == 0:
358             element = ['']
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]))
363         else:
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):
367                 com = ''
368                 for text in x['raw'].splitlines(True):                  
369                     for word in element:                        
370                         if text.find(word) > -1:
371                             com = com +'<p style=background-color:yellow>'+text+'<br></p>'  
372                             break                          
373                     else:
374                         com = com+'<p>'+text+'<br></p>'
375                 x['comment'] = com
376                 yield x       
377         else:
378             for x in table.search(query):
379                 yield x
380                                         
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)
384     
385 class HeadlineApi(tornado.web.RequestHandler):
386     def get(self):
387         response = {}
388         for x in self.application.db.tables():
389             if x != '_default':
390                 response[x] = self.get_data(x)           
391         self.write(json.dumps(response,ensure_ascii=False))
392     
393     def get_data(self,dbname):
394         table = self.application.db.table(dbname)
395         i = len(table)
396         if i == 0:
397             return {}
398         else:
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]}
401         
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))
407             if response == None:
408                 response = {}
409             else:
410                 del response['comment']
411             self.write(json.dumps(response,ensure_ascii=False))
412         else:
413             tornado.web.HTTPError(404)
414     
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})
421         
422 class HelpHandler(tornado.web.RequestHandler):
423     def get(self):
424         self.render('help.htm',req='') 
425         
426     def post(self):
427         com = self.get_argument('help','')
428         text = ''
429         for line in com.splitlines():
430             text +='<p>'+line
431         table = self.application.db.table('master')
432         time = datetime.now()
433         table.insert({'comment':text,'time':time.strftime('%Y/%m/%d %H:%M')})
434         if com == '':
435             req = ''
436         else:
437             req = '送信しました'
438         self.render('help.htm',req=req)
439         
440 class MasterHandler(BaseHandler):
441     @tornado.web.authenticated
442     def get(self):
443         if self.current_user == b'admin':
444             com = self.application.db.table('master').all()
445             self.render('master.htm',com=com)
446         else:
447             raise tornado.web.HTTPError(404)
448         
449 class AlertHandler(UserHandler):
450     def get(self):
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)
463     
464     def post(self):
465         id = int(self.get_argument('num'))
466         table = self.application.db.table('temp')
467         tb = table.get(eid=id)
468         link = tb['link']
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']
474             del tb['date']
475             table = self.application.db.table('master')
476             table.insert(tb)
477         self.redirect(link)
478         
479 class Application(tornado.web.Application):    
480     def __init__(self):
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=',
492                         'xsrf_cookies':True,
493                         'debug':True,
494                         'login_url':'/login'
495                         }
496         tornado.web.Application.__init__(self,handlers,**settings)
497  
498     def gpos(self,dbname,page):
499         params = self.db.get(where('kinds') == 'conf')
500         pos = int(page)
501         if pos <= 0:
502             pos = 0
503         elif (pos-1)*params['count'] >= len(self.db.table(dbname)):
504             pos = 0
505         return pos
506     
507     def collection(self,name):
508         if name in self.db.tables():
509             return True
510         else:
511             return False
512
513 class static():
514     json = 'static/db/db.json'
515     bak = 'static/db/bak.json'
516
517 st = static()
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()