OSDN Git Service

login value
[pybbs/pybbs.git] / index.py
1
2 import os.path,urllib
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
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         rec = sorted(table.all(),key=lambda x: x['number'])[start:start+i]
61         if len(table) >= 10*i:
62             self.render('modules/full.htm',position=pos,records=rec,data=params,db=dbname)
63             return
64         if (dbname == params['info name'])and(self.current_user != b'admin'):
65             self.render('modules/info.htm',position=pos,records=rec,data=params,db=dbname)
66         else:
67             self.render('modules/index.htm',position=pos,records=rec,data=params,username=na,db=dbname,aikotoba=rule)
68         
69 class LoginHandler(BaseHandler):
70     def get(self):
71         query = urllib.parse.urlparse(self.request.uri).query
72         qs = urllib.parse.parse_qs(query)      
73         if 'db' in qs:
74             dbname = qs['db']
75         elif 'next' in qs:
76             s = qs['next'][0]
77             dbname = urllib.parse.parse_qs(s[s.find('?')+1:])['db']
78         else:
79             dbname = {}
80         if len(dbname) == 0:
81             raise tornado.web.HTTPError(404)
82             return
83         else:
84             self.render('login.htm',db=dbname[0])
85         
86     def post(self):
87         pw = self.application.db.get(where('kinds') == 'conf')
88         if self.get_argument('password') == pw['password']:
89             self.set_current_user('admin')
90         dbname = self.get_argument('record')
91         self.redirect('/'+dbname+'/admin/0/')
92         
93 class LogoutHandler(BaseHandler):
94     def get(self):
95         self.clear_current_user()
96         self.redirect('/login')
97         
98 class NaviHandler(tornado.web.RequestHandler):              
99     def get(self):
100         col,na = self.name()
101         self.render('top.htm',coll=col,name=na,full=self.full)
102         
103     def name(self):
104         names = self.application.db.tables()
105         names.remove('_default')
106         na = self.application.db.get(where('kinds') == 'conf')['info name']
107         if na in names:
108             names.remove(na)
109         else:
110             na = ''
111         return sorted(names),na
112                 
113     def full(self,dbname):
114         if dbname in self.application.db.tables():
115             i = 10*self.application.db.get(where('kinds') == 'conf')['count']
116             table = self.application.db.table(dbname)
117             if len(table) >= i:
118                 return True
119         return False
120
121 class TitleHandler(NaviHandler):
122     def get(self):
123         rec = sorted(self.title(),key=lambda x: x['date2'])
124         self.render('title.htm',coll=rec,full=self.full)  
125         
126     def title(self):
127         names = self.application.db.tables()
128         names.remove('_default')
129         for x in names:
130             item = {}
131             item['name'] = x
132             table = self.application.db.table(x)
133             i = len(table)
134             item['count'] = i            
135             if table.contains(where('number') == 1) == True:
136                 s = table.get(where('number') == 1)['title']
137             else:
138                 s = ''
139             item['title'] = s   
140             if i == 0:
141                 item['date'] = ''
142                 item['date2'] = 0
143             else:
144                 rec = sorted(table.all(),key=lambda k: k['number'])
145                 s = rec[i-1]['date']
146                 item['date'] = s
147                 i = datetime.strptime(s,'%Y/%m/%d %H:%M')
148                 year = datetime.now().year-i.year
149                 if year == 0:
150                     j = 800
151                 elif year == 1:
152                     j = 400
153                 else:
154                     j = 0
155                 item['date2'] = j+31*(i.month-1)+i.day
156             yield item
157         
158 class RegistHandler(tornado.web.RequestHandler):
159     def post(self,dbname):
160         if self.application.collection(dbname) == False:
161             raise tornado.web.HTTPError(404)
162             return
163         self.database = dbname
164         rec = self.application.db.get(where('kinds') == 'conf')
165         words = rec['bad_words']
166         out = rec['out_words']
167         na = self.get_argument('name')
168         sub = self.get_argument('title')
169         com = self.get_argument('comment',None,False)
170         text = ''
171         i = 0
172         url = []
173         error = ''
174         for word in out:
175             if word in com:
176                 error = error + u'禁止ワード.'
177                 break
178         for line in com.splitlines(True):
179             if error != '':
180                 break
181             for word in words:
182                 if word in line.lower():
183                     error = error + u'タグ違反.('+word+')'       
184             i += len(line)   
185             obj = re.finditer('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', line)
186             for x in obj:
187                 if x.group() not in url:
188                     url.append(x.group())
189             if re.match(' ',line):
190                 line = line.replace(' ','&nbsp;',1)
191             text = text+'<p>'+self.link(line)+'<br></p>'
192         s = ''
193         for x in url:
194             s = s+'<tr><td><a class=livepreview target=_blank href={0}>{0}</a></td></tr>'.format(x)
195         if s:
196             text = text+'<table><tr><td>検出URL:</td></tr>'+s+'</table>'
197         pw = self.get_argument('password')
198         if i == 0:
199             error = error + u'本文がありません.'
200         elif i > 1000:
201             error = error +u'文字数が1,000をこえました.'
202         article = self.application.db.table(dbname)
203         if len(article) == 0:
204             no = 1
205         else:
206             item = sorted(article.all(),key=lambda x: x['number'])[len(article)-1]
207             no = item['number']+1
208         if error == '':
209             if not na:
210                 na = u'誰かさん'
211             s = datetime.now()
212             reg = {'number':no,'name':na,'title':sub,'comment':text,'raw':com,'password':pw,'date':s.strftime('%Y/%m/%d %H:%M')}
213             article.insert(reg)
214             self.set_cookie('username',tornado.escape.url_escape(na))
215             self.redirect('/'+dbname+'#article')
216         else:
217             self.render('regist.htm',content=error)
218     
219     def link(self,command):
220         i = 0
221         text = ''
222         obj = re.finditer('>>[0-9]+',command)
223         for x in obj:
224             s = '<a class=minpreview data-preview-url=/{0}?key={1} href=/{0}/userdel?job={1}>>>{1}</a>'.format(self.database,x.group()[2:])
225             text = text+command[i:x.start()]+s
226             i = x.end()
227         else:
228             text = text+command[i:]
229         return text
230     
231 class AdminHandler(BaseHandler):
232     @tornado.web.authenticated               
233     def get(self,dbname,page):
234         if dbname == '':
235             dbname = self.get_argument('record','')
236         if self.application.collection(dbname) == False:
237             raise tornado.web.HTTPError(404)
238             return
239         table = self.application.db.table(dbname) 
240         rec = sorted(table.all(),key=lambda x: x['number'])                   
241         mente = self.application.db.get(where('kinds') == 'conf')
242         if mente['mentenance'] == True:
243             check = 'checked=checked'
244         else:
245             check = ''
246         pos = self.application.gpos(dbname,page)
247         i = mente['count']
248         start = (pos-1)*i
249         if start < 0:
250             start = len(table)-i
251             if start < 0:
252                 start = 0
253         self.render('modules/admin.htm',position=pos,records=rec[start:start+i],mente=check,password=mente['password'],db=dbname)
254
255 class AdminConfHandler(BaseHandler):
256     @tornado.web.authenticated
257     def post(self,dbname,func):
258         if func == 'set':
259             param = self.application.db.get(where('kinds') == 'conf')['mentenance']
260             if self.get_argument('mente','') == 'on':
261                 mente = True
262                 if param != mente:
263                     self.store()
264             else:
265                 mente = False  
266                 if param != mente:
267                     self.restore()
268             word = self.get_argument('pass','')
269             if word == '':
270                 self.render('regist.htm',content='パスワードを設定してください')
271                 return
272             else:
273                 self.application.db.update({'mentenance':mente,'password':word},where('kinds') == 'conf')  
274         elif func == 'del':
275             table = self.application.db.table(dbname)
276             for x in self.get_arguments('item'):
277                 table.remove(where('number') == int(x))
278         self.redirect('/'+dbname+'/admin/0/')
279         
280     def store(self):
281         self.application.db.close()
282         shutil.copy(st.json,st.bak)
283         self.application.db = TinyDB(st.json)
284         
285     def restore(self):
286         database = self.application.db
287         bak = TinyDB(st.bak)
288         for x in database.tables():
289             if self.application.collection(x) == True:
290                 database.purge_table(x)
291                 if x in bak.tables():
292                     table = database.table(x)
293                     table.insert_multiple(bak.table(x).all())
294           
295 class UserHandler(tornado.web.RequestHandler):
296     table = None
297     def get(self,dbname):
298         self.table = self.application.db.table(dbname)
299         q = self.get_query_argument('job','0',strip=True)
300         num = self.page(int(q))        
301         if num == '':
302             self.redirect('/{0}#{1}'.format(dbname,q))           
303         else:
304             self.redirect('/{0}{1}#{2}'.format(dbname,num,q))
305         
306     def post(self,dbname):
307         number = self.get_argument('number')
308         if number.isdigit() == True:
309             num = int(number)
310             pas = self.get_argument('password')
311             self.table = self.application.db.table(dbname)
312             qwr = Query()
313             obj = self.table.get(qwr.number == num)
314             if obj and(obj['password'] == pas):
315                 self.table.update({'title':u'削除されました','name':'','comment':u'<i><b>投稿者により削除されました</b></i>'},qwr.number == num)
316                 self.redirect('/{0}{1}#{2}'.format(dbname,self.page(num),number))
317             else:
318                 self.redirect('/'+dbname)
319                 
320     def page(self,number):
321         if self.table != None:
322             rec = self.table.count(where('number') <= number)
323             conf = self.application.db.get(where('kinds') == 'conf')
324             if len(self.table)-rec >= conf['count']:
325                 return '/'+str(1+rec//conf['count'])+'/'
326             else:
327                 return ''
328       
329 class SearchHandler(tornado.web.RequestHandler):       
330     def post(self,dbname):
331         arg = self.get_argument('word1')
332         self.word = arg 
333         self.radiobox = self.get_argument('filter')      
334         rec = sorted(self.search(dbname),key=lambda x: x['number'])
335         self.render('modules/search.htm',records=rec,word1=arg,db=dbname)
336     
337     def get(self,dbname):
338         if self.application.collection(dbname) == False:
339             raise tornado.web.HTTPError(404)
340             return
341         self.render('modules/search.htm',records=[],word1='',db=dbname)
342         
343     def search(self,dbname):
344         table = self.application.db.table(dbname)    
345         element = self.word.split()
346         if len(element) == 0:
347             element = ['']
348         while len(element) < 3:
349             element.append(element[0])
350         if self.radiobox == 'comment':
351             query = (Query().raw.search(element[0])) | (Query().raw.search(element[1])) | (Query().raw.search(element[2]))
352         else:
353             query = (Query().name == element[0]) | (Query().name == element[1]) | (Query().name == element[2])
354         if self.radiobox == 'comment':    
355             for x in table.search(query):
356                 com = ''
357                 for text in x['raw'].splitlines(True):                  
358                     for word in element:                        
359                         if text.find(word) > -1:
360                             com = com +'<p style=background-color:yellow>'+text+'<br></p>'  
361                             break                          
362                     else:
363                         com = com+'<p>'+text+'<br></p>'
364                 x['comment'] = com
365                 yield x       
366         else:
367             for x in table.search(query):
368                 yield x
369                                         
370 class FooterModule(tornado.web.UIModule):
371     def render(self,number,url,link):
372         return self.render_string('modules/footer.htm',index=number,url=url,link=link)
373     
374 class HeadlineApi(tornado.web.RequestHandler):
375     def get(self):
376         response = {}
377         for x in self.application.db.tables():
378             if x != '_default':
379                 response[x] = self.get_data(x)           
380         self.write(json.dumps(response,ensure_ascii=False))
381     
382     def get_data(self,dbname):
383         table = self.application.db.table(dbname)
384         i = len(table)
385         if i == 0:
386             return {}
387         else:
388             rec = sorted(table.all(),key=lambda x: x['number'])[i-1]
389             return {'number':rec['number'],'title':rec['title'],'name':rec['name'],'comment':rec['raw'][0:19]}
390         
391 class ArticleApi(tornado.web.RequestHandler):
392     def get(self,dbname,number):
393         if self.application.collection(dbname) == True:
394             table = self.application.db.table(dbname)
395             response = table.get(where('number') == int(number))
396             if response == None:
397                 response = {}
398             else:
399                 del response['comment']
400             self.write(json.dumps(response,ensure_ascii=False))
401         else:
402             tornado.web.HTTPError(404)
403     
404     def post(self,dbname):
405         name = self.get_argument('name',u'誰かさん')
406         title = self.get_argument('title',u'タイトルなし')
407         comment = self.get_argument('comment')
408         table = self.application.db.table(dbname)
409         table.insert({'name':name,'title':title,'comment':comment})
410         
411 class Application(tornado.web.Application):    
412     def __init__(self):
413         self.db = TinyDB(st.json)             
414         handlers = [(r'/',NaviHandler),(r'/login',LoginHandler),(r'/logout',LogoutHandler),(r'/title',TitleHandler),
415                     (r'/headline/api',HeadlineApi),(r'/read/api/([a-zA-Z0-9_]+)/([0-9]+)',ArticleApi),(r'/write/api/([a-zA-Z0-9_]+)',ArticleApi),
416                     (r'/([a-zA-Z0-9_]+)',IndexHandler),(r'/([a-zA-Z0-9_]+)/([0-9]+)/*',IndexHandler),
417                     (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),
418                     (r'/([a-zA-Z0-9_]+)/search',SearchHandler),(r'/([a-zA-Z0-9_]+)/regist',RegistHandler)]
419         settings = {'template_path':os.path.join(os.path.dirname(__file__),'templates'),
420                         'static_path':os.path.join(os.path.dirname(__file__),'static'),
421                         'ui_modules':{'Footer':FooterModule},
422                         'cookie_secret':'bZJc2sWbQLKo6GkHn/VB9oXwQt8SOROkRvJ5/xJ89Eo=',
423                         'xsrf_cookies':True,
424                         'debug':True,
425                         'login_url':'/login'
426                         }
427         tornado.web.Application.__init__(self,handlers,**settings)
428  
429     def gpos(self,dbname,page):
430         params = self.db.get(where('kinds') == 'conf')
431         pos = int(page)
432         if pos <= 0:
433             pos = 0
434         elif (pos-1)*params['count'] >= len(self.db.table(dbname)):
435             pos = 0
436         return pos
437     
438     def collection(self,name):
439         if name in self.db.tables():
440             return True
441         else:
442             return False
443
444 class static():
445     json = 'static/db/db.json'
446     bak = 'static/db/bak.json'
447
448 st = static()
449 if __name__ == '__main__':
450     tornado.options.parse_command_line()
451     http_server = tornado.httpserver.HTTPServer(Application())
452     http_server.listen(options.port)
453     tornado.ioloop.IOLoop.instance().start()