OSDN Git Service

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