OSDN Git Service

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