OSDN Git Service

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