OSDN Git Service

問題発覚
[pybbs/pybbs.git] / pybbs.py
index e41bf1e..a714a2d 100644 (file)
--- a/pybbs.py
+++ b/pybbs.py
@@ -2,7 +2,7 @@
 import os,re,glob
 from tornado import escape,web,ioloop,httpserver,httpclient
 import pymongo, urllib
-from datetime import datetime,date
+from datetime import datetime,timedelta
 import json
 from bson.objectid import ObjectId #don't remove
 from linebot.api import LineBotApi
@@ -22,69 +22,86 @@ class BaseHandler(web.RequestHandler):
         self.clear_cookie('admin_user')
 
 class IndexHandler(BaseHandler):
-    def get(self,dbname,page='0'):
+    def main(self,dbname,page):
         params = self.application.db['params'].find_one({'app':'bbs'})
-        if params['mentenance'] == True:
+        if params['mentenance'] is True:
             self.render('mentenance.htm',title=params['title'],db=dbname)
             return
-        if self.application.collection(dbname) == False:
+        if dbname not in self.application.mylist():
             if self.current_user == b'admin':
                 coll = self.application.db[dbname]
                 coll.insert({})
                 coll.remove({})
             else:
                 raise web.HTTPError(404)
-                return
         key = self.get_argument('key','')
-        if key:
+        if key != '':
             table = self.application.db[dbname]
             rec = table.find_one({'number':int(key)})
             if rec:
                 self.render('article.htm',record=rec)
                 return
             else:
-                web.HTTPError(404)
-                return
-        i = params['count']      
-        rule = escape.url_unescape(self.get_cookie('aikotoba',''))
-        na = escape.url_unescape(self.get_cookie('username',u'誰かさん'))
-        pos = self.application.gpos(dbname,page)
+                raise web.HTTPError(404)
+        self.rule = escape.url_unescape(self.get_cookie('aikotoba',''))
+        self.na = escape.url_unescape(self.get_cookie('username',u'誰かさん'))
+        self.pos = self.application.gpos(dbname,page)
         table = self.application.db[dbname]
-        start = (pos-1)*i
+        i = params['count']
+        start = (self.pos-1)*i
         if start < 0:
             start = table.count()-i
             if start < 0:
                 start = 0
         rec = table.find()
-        bool = (dbname == params['info name']) 
-        if bool == True:
+        self.bool = (dbname == params['info name'])
+        if self.bool is True:
             rec.sort('number',-1)
         else:
             rec.sort('number')
-        rec.skip(start).limit(i)
-        if table.count() >= 10*i:
-            self.render('modules/full.htm',position=pos,records=rec,data=params,db=dbname)
-            return
-        if (bool == True)and(self.current_user != b'admin'):
-            self.render('modules/info.htm',position=pos,records=rec,data=params,db=dbname)
+        self.rec = rec.skip(start).limit(i)
+
+    def get(self,dbname,page='0'):
+        self.main(dbname,page)
+        db = self.application.db
+        table = db[dbname].find()
+        params = db['params'].find_one({'app':'bbs'})
+        if table.count() >= 10*params['count']:
+            self.render('modules/full.htm',position=self.pos,records=self.rec,data=params,db=dbname)
+        if self.bool is True and self.current_user != b'admin':
+            self.render('modules/info.htm',position=self.pos,records=self.rec,data=params,db=dbname,error='')
         else:
-            self.render('modules/index.htm',position=pos,records=rec,data=params,username=na,db=dbname,aikotoba=rule)
-        
+            self.render_admin(dbname)
+
+    def render_admin(self,dbname,title='',com='',er='',img='',ch='checked'):
+        t = self.get_argument('img','')
+        params = self.application.db['params'].find_one({'app':'bbs'})
+        if self.current_user == b'admin':
+            s = '<label><p>URL </p><input name="img" placeholder="src=http://" value=' + t + '></label>'
+        else:
+            s = '<input type=hidden>'
+        self.render('modules/index.htm',position=self.pos,records=self.rec,data=params,username=self.na,title=title,
+            comment=com,db=dbname,aikotoba=self.rule,error=er+img,check=ch,admin=s)
+
 class LoginHandler(BaseHandler):
     def get(self):
-        query = self.get_query_argument('next','')            
+        info = self.application.db['params'].find_one({'app':'bbs'})
+        query = self.get_query_argument('next','/'+info['info name'])
         i = query[1:].find('/')
         if i == -1:
             qs = query[1:]
         else:
             qs = query[1:i+1]
-        self.render('login.htm',db=qs)
+        self.render('login.htm',db=escape.url_unescape(qs))
         
     def post(self):
+        dbname = self.get_argument('record','')
+        if dbname == '':
+            self.redirect('/login')
+            return
         pw = self.application.db['params'].find_one({'app':'bbs'})
         if self.get_argument('password') == pw['password']:
             self.set_current_user('admin')
-        dbname = self.get_argument('record')
         if dbname == 'master':
             self.redirect('/master')
         else:
@@ -101,27 +118,24 @@ class JumpHandler(BaseHandler):
         self.redirect('/')
         
 class NaviHandler(web.RequestHandler):
-    def get(self):  
-        if self.application.collection('params') == False:
+    def get(self):
+        if 'params' not in self.application.mylist():
             item = {"mentenance":False,"out_words":[u"阿保",u"馬鹿",u"死ね"],"password":"admin",
-                    "title2":"<h1 style=color:gray;text-align:center>pybbs</h1>",
-                    "bad_words":["<style","<link","<script","<img"],"count":30,
-                    "title":"pybbs","info name":"info",'app':'bbs'}       
+                    "title2":"<h1 style=color:maroon;font-style:italic;text-align:center>とるね~ど号</h1>",
+                    "bad_words":["<style","<link","<script","<img","<a"],"count":30,
+                    "title":u"とるね~ど号","info name":"info",'app':'bbs'}
             self.application.db['params'].insert(item)
-        coll,na = self.name()                
+            self.application.db['info'].find()
+        table = self.application.db['params'].find_one({'app':'bbs'})
+        if table['mentenance'] is True:
+            self.render('mentenance.htm',title=table['title'],db=table['info name'])
+            return
+        coll = self.application.coll()
+        na = table['info name']
         self.render('top.htm',coll=coll,name=na,full=self.full)
-                  
-    def name(self):
-        coll = sorted(self.application.coll(),key=str.lower)
-        na = self.application.db['params'].find_one({'app':'bbs'})['info name']
-        if na in coll:
-            coll.remove(na)
-        else:
-            na = ''
-        return coll,na
-            
+
     def full(self,dbname):
-        if dbname in self.application.db.collection_names():
+        if dbname in self.application.coll():
             i = 10*self.application.db['params'].find_one({'app':'bbs'})['count']
             table = self.application.db[dbname]
             if table.count() >= i:
@@ -134,8 +148,7 @@ class TitleHandler(NaviHandler):
         self.render('title.htm',coll=rec,full=self.full)  
         
     def title(self):
-        name = self.application.coll()
-        for x in name:
+        for x in self.application.coll():
             item = {}
             item['name'] = x
             table = self.application.db[x]
@@ -165,15 +178,14 @@ class TitleHandler(NaviHandler):
                 item['date2'] = j+31*(i.month-1)+i.day
             yield item
         
-class RegistHandler(web.RequestHandler):
+class RegistHandler(IndexHandler):
     def post(self,dbname):
-        if self.application.collection(dbname) == False:
+        self.main(dbname,'0')
+        if dbname not in self.application.coll(info=True):
             raise web.HTTPError(404)
-            return
-        self.database = dbname
-        rec = self.application.db['params'].find_one({'app':'bbs'})
-        words = rec['bad_words']
-        out = rec['out_words']
+        params = self.application.db['params'].find_one({'app':'bbs'})
+        words = params['bad_words']
+        out = params['out_words']
         rule = self.get_argument('aikotoba')
         na = self.get_argument('name')
         sub = self.get_argument('title')
@@ -182,18 +194,18 @@ class RegistHandler(web.RequestHandler):
         i = 0
         url = []
         error = ''
-        if rule != u'げんき':
-            error = u'合言葉未入力.'
-        for word in out:
-            if word in com:
-                error += u'禁止ワード.'
-                break
+        kinsi = False
         for line in com.splitlines():
-            if error:
-                break
+            if kinsi is False:
+                for word in out:
+                    if word in line:
+                        error += u'禁止ワード.<br>'
+                        kinsi = True
+                        break
             for word in words:
                 if word in line.lower():
-                    error += u'タグ違反.('+word+')'       
+                    tag = escape.xhtml_escape(word)
+                    error += u'タグ違反.('+tag+')<br>'
             i += len(line)
             obj = re.finditer('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', line)
             for x in obj:
@@ -208,17 +220,19 @@ class RegistHandler(web.RequestHandler):
             if j > 0: 
                 line = line.replace(' ','&nbsp;',j)     
             if len(line) == 0:
-                text += '<br>'
+                text += '<p><br>\n</p>'
             else:
-                text += '<p>'+self.link(line)+'</p>'
+                text += '<p>'+self.link(line,dbname)+'\n</p>'
+        if rule != u'げんき':
+            error += u'合言葉未入力.<br>'
         s = ''
         for x in url:
             s = s+'<tr><td><a href={0} class=livepreview target=_blank>{0}</a></td></tr>'.format(x)
-        if s:         
-            text = text+'<table><tr><td>検出url:</td></tr>'+s+'</table>';
+        if s != '':
+            text = text+'<table><tr><td>検出url:</td></tr>'+s+'</table>'
         pw = self.get_argument('password')
         if i > 1000:
-            error += +u'文字数が1,000をこえました.'
+            error += u'文字数が1,000をこえました.<br>'
         if na == '':
             na = u'誰かさん'
         if sub == '':
@@ -230,22 +244,46 @@ class RegistHandler(web.RequestHandler):
             items = article.find()
             item = items.sort('number')[article.count()-1]
             no = item['number']+1
+        s = datetime.now()
+        k = '%Y%m%d%H%M%S'
+        if self.get_argument('show', 'false') == 'true':
+            ch = 'checked'
+        else:
+            ch = ''
+            t = self.get_cookie('time')
+            if t and s - datetime.strptime(escape.url_unescape(t),k) < timedelta(seconds=10):
+                error += u'二重送信.'
+        img = self.get_argument('img','')
+        if img:
+            img = '<img src="' + escape.url_unescape(img) + '">'
         if error == '':
-            self.set_cookie('aikotoba',escape.url_escape(rule))
-            s = datetime.now()
-            reg = {'number':no,'name':na,'title':sub,'comment':text,'raw':com,'password':pw,'date':s.strftime('%Y/%m/%d %H:%M')}
-            article.insert(reg)
-            self.set_cookie('username',escape.url_escape(na))
-            self.redirect('/'+dbname+'#article')
+            if ch == 'checked':
+                error = '<p style=font-size:2.5em;color:blue>↓↓プレビュー↓↓</p>\n' + text
+                ch = ''
+            else:
+                com += img
+                text += img
+                reg = {'number': no, 'name': na, 'title': sub, 'comment': text, 'raw': com, 'password': pw,
+                    'date': s.strftime('%Y/%m/%d %H:%M')}
+                article.insert(reg)
+                self.set_cookie('aikotoba', escape.url_escape(rule))
+                self.set_cookie('username', escape.url_escape(na))
+                self.set_cookie('time',escape.url_escape(s.strftime(k)))
+                self.redirect('/' + dbname + '#article')
+                return
         else:
-            self.render('regist.htm',content=error)
-    
-    def link(self,command):
+            error = '<p style=color:red>' + error + '</p>'
+        self.na = na
+        self.rule = rule
+        self.pos = 0
+        self.render_admin(dbname,title=sub,com=com,er=error,ch=ch,img=img)
+
+    def link(self,command,database):
         i = 0
         text = ''
         obj = re.finditer('>>[0-9]+',command)
         for x in obj:
-            s = '<a class=minpreview data-preview-url=/{0}?key={1} href=/{0}/userdel?job={1}>>>{1}</a>'.format(self.database,x.group()[2:])
+            s = '<a class=minpreview data-preview-url=/{0}?key={1} href=/{0}/userdel?job={1}>>>{1}</a>'.format(database,x.group()[2:])
             text = text+command[i:x.start()]+s
             i = x.end()
         else:
@@ -257,13 +295,10 @@ class AdminHandler(BaseHandler):
     def get(self,dbname,page='0'):
         if dbname == '':
             dbname = self.get_argument('record','')
-        if self.application.collection(dbname) == False:
-            raise web.HTTPError(404)
-            return
-        table = self.application.db[dbname] 
+        table = self.application.db[dbname]
         rec = table.find().sort('number')                   
         mente = self.application.db['params'].find_one({'app':'bbs'})
-        if mente['mentenance'] == True:
+        if mente['mentenance'] is True:
             check = 'checked=checked'
         else:
             check = ''
@@ -303,37 +338,27 @@ class AdminConfHandler(BaseHandler):
 class UserHandler(web.RequestHandler):
     def get(self,dbname):
         q = self.get_query_argument('job','0',strip=True)
-        num = self.page(dbname,int(q))
-        self.redirect('/{0}{1}#{2}'.format(dbname,num,q))
+        self.redirect(self.application.page(dbname,q))
         
     def post(self,dbname):
         number = self.get_argument('number')
-        if number.isdigit() == True:
+        if number.isdigit() is True:
             num = int(number)
+            url = self.application.page(dbname,number)
             if 'password' in self.request.arguments.keys():
                 pas = self.get_argument('password')
             else:
-                num = self.page(dbname,num)
-                self.redirect('/{0}{1}#{2}'.format(dbname, num, number))
+                self.redirect(url)
                 return
             table = self.application.db[dbname]
             obj = table.find_one({'number':num})
             if obj and(obj['password'] == pas):
                 table.update({'number':num},{'$set':{'title':u'削除されました','name':'','comment':u'<i><b>投稿者により削除されました</b></i>','raw':''}})
-                self.redirect('/'+dbname+self.page(dbname,num)+'#'+number)
-            else:
-                self.redirect('/'+dbname)
-                
-    def page(self,table,number):
-        tb = self.application.db[table]
-        rec = tb.find({'number':{'$lte':number}}).count()
-        conf = self.application.db['params'].find_one({'app':'bbs'})
-        if tb.find().count()-rec >= conf['count']:
-            return '/'+str(1+rec//conf['count'])+'/'
-        else:
-            return ''
-      
-class SearchHandler(web.RequestHandler):       
+                self.redirect(url)
+                return
+        self.redirect('/'+dbname)
+
+class SearchHandler(web.RequestHandler):
     def post(self,dbname=''):
         arg = self.get_argument('word1')
         self.word = arg[:]
@@ -351,9 +376,8 @@ class SearchHandler(web.RequestHandler):
         self.render('modules/search.htm',records=rec,word1=arg,db=dbname)
 
     def get(self,dbname=''):
-        if self.application.collection(dbname) == False and dbname != '':
+        if dbname not in self.application.coll(info=True) and dbname != '':
             raise web.HTTPError(404)
-            return
         self.render('modules/search.htm',records=[],word1='',db=dbname)
     
     def search(self,dbname):
@@ -427,23 +451,26 @@ class MasterHandler(BaseHandler):
     def get(self):
         if self.current_user == b'admin':
             com = self.application.db['master'].find()
-            self.render('master.htm',com=com)
+            sum = self.application.db['temp'].find().count()
+            self.render('master.htm',com=com,sum=sum)
         else:
             raise web.HTTPError(404)
     
-class AlertHandler(UserHandler):
+class AlertHandler(web.RequestHandler):
     def get(self):
         db = self.get_query_argument('db')
         num = self.get_query_argument('num')
-        tb = self.application.db[db].find_one({'number':int(num)})
+        table = self.application.db[db]
+        tb = table.find_one({'number':int(num)})
         com = tb['comment']
         time = datetime.now().strftime('%Y/%m/%d')
-        s = self.page(db,int(num))
-        link = '/'+db+s+'#'+num  
+        link = self.application.page(db,num)
         jump = '<p><a href={0}>{0}</a>'.format(link)
-        result = self.application.db['temp'].insert(
-            {'comment':com+jump,'time':time,'link':link,
-             'date':date.weekday(datetime.now()),'db':db,'num':num})
+        d = datetime.now().weekday()
+        table = self.application.db['temp']
+        table.remove({'date':{'$ne':d}})
+        result = table.insert(
+            {'comment':com+jump,'time':time,'link':link,'date':d,'db':db,'num':num})
         self.render('alert.htm',com=com+jump,num=str(result))
         
     def post(self):
@@ -453,7 +480,6 @@ class AlertHandler(UserHandler):
         link = tb['link']
         com = self.get_argument('com')
         table.remove({'_id':id})
-        table.remove({'date':{'$ne':date.weekday(datetime.now())}})
         if self.get_argument('cancel','') == 'cancel':
             self.redirect(link)
             return
@@ -470,16 +496,18 @@ class CleanHandler(web.RequestHandler):
         table = self.application.db['master']
         if bool == 'true':
             table.remove()
+            self.application.db['temp'].remove()
         elif bool == 'false':
             for x in list(table.find()):           
-                if not 'number' in x.keys():
+                if not 'num' in x.keys():
                     table.remove({'_id':x['_id']})
                 else:
                     item = self.application.db[x['db']].find_one({'number':int(x['num'])})
-                    if not item or item['raw'] == '':
+                    if (not item)or(item['raw'] == ''):
                         table.remove({'_id':x['_id']})   
         com = self.application.db['master'].find()
-        self.render('master.htm', com=com)    
+        sum = self.application.db['temp'].find().count()
+        self.render('master.htm', com=com, sum=sum)
                                         
 class FooterModule(web.UIModule):
     def render(self,number,url,link):
@@ -500,34 +528,31 @@ class HeadlineApi(web.RequestHandler):
         
 class ArticleApi(web.RequestHandler):
     def get(self,dbname,number):
-        if self.application.collection(dbname) == True:
+        response = None
+        if dbname in self.application.coll():
             table = self.application.db[dbname]
             response = table.find_one({'number':int(number)})      
-        if response == None:
-           response = {}
+        if not response:
+            response = {}
         else:
             del response['_id']
             del response['comment']
             del response['password']
         self.write(json.dumps(response,ensure_ascii=False))      
-            
-    def post(self,dbname,name,title,article):
-        coll = self.application.db[dbname] 
-        coll.insert({'name':name,'title':title,'comment':article})
-        
+
 class ListApi(web.RequestHandler):
     def get(self,dbname):
-        if self.application.collection(dbname) == True:
+        response = None
+        if dbname in self.application.coll():
             table = self.application.db[dbname]
             response = {}
             for data in table.find().sort('number'):
                 response[data['number']] = data['raw'][0:20]
-        if response == None:
+        if not response:
             response = {}
         self.write(json.dumps(response,ensure_ascii=False))
-           
-class WebHookHandler(web.RequestHandler):        
+
+class WebHookHandler(web.RequestHandler):
     def main(self, no):
         #pz = pytz.timezone('Asia/Tokyo')
         now = datetime.now()#pz)
@@ -566,17 +591,17 @@ class WebHookHandler(web.RequestHandler):
     def help(self):
         s = '-*-database names-*-\n'
         out = ['objectlabs-system','objectlabs-system.admin.collections','users_bot']
-        for x in self.application.db.collection_names(include_system_collections=False):
-            if not x in out and x[-4:] == '_bot':
+        for x in self.application.mylist():
+            if x not in out and x[-4:] == '_bot' and x != '_bot':
                 s += x[:-4]+'\n'
         return s
     
     def setting(self, dbname):
-        dbname = dbname.lower()
-        ca = self.application.db.collection_names(include_system_collections=False)
+        dbname = dbname.lower()+'_bot'
+        ca = self.application.mylist()
         if 'users_bos' in ca:
             ca.remove('users_bot')
-        if dbname[-4:] == '_bot' and dbname in ca:
+        if dbname in ca:
             db = self.application.db['users_bot']
             item = db.find_one({'name':self.uid})
             if item['dbname'] == dbname:
@@ -590,7 +615,7 @@ class WebHookHandler(web.RequestHandler):
         db = self.application.db['users_bot']
         item = db.find_one({'name':self.uid})
         x = item['dbname']
-        return db[x], x
+        return self.application.db[x], x[:-4]
                           
     def post(self):
         '''
@@ -613,9 +638,18 @@ class WebHookHandler(web.RequestHandler):
                     return
                 elif event['type'] != 'message' or event['message']['type'] != 'text':
                     return
-                if not bot in self.application.db.collection_names() or not self.application.db[bot].find_one({'name':self.uid}):
+                item = self.application.db['params'].find_one({'app':'bot'})
+                if item:
+                    de = item['default']
+                else:
+                    de = '_bot'         
+                if item and 'access_token' in item.keys():
+                    token = item['access_token']
+                else:      
+                    token =self.application.tk
+                if bot not in self.application.mylist() or not self.application.db[bot].find_one({'name':self.uid}):
                     db = self.application.db[bot]
-                    db.insert({'name':self.uid, 'dbname':'glove'})
+                    db.insert({'name':self.uid, 'dbname':de})
                 x = event['message']['text']     
                 if self.setting(x):
                     te = u'設定完了.'
@@ -623,21 +657,30 @@ class WebHookHandler(web.RequestHandler):
                     te = self.help()
                 else:
                     te = self.main(x)
-                item = self.application.db['params'].find_one({'app':'bot'})
-                if item and 'access_token' in item.keys():
-                    linebot = LineBotApi(item['access_token'])            
-                    linebot.reply_message(event['replyToken'], TextSendMessage(text=te))
-                else:
-                    dic['events']['message']['text'] = te
-                    self.write(escape.json_encode(dic))
+                linebot = LineBotApi(token)            
+                linebot.reply_message(event['replyToken'], TextSendMessage(text=te))
 
 class InitHandler(web.RequestHandler):
     def get(self):        
+        de = self.get_argument('default', '')     
+        if de == '':
+            names = self.application.mylist()
+            db = []
+            for x in names:
+                if x[-4:] == '_bot' and x != 'users_bot':
+                    db.append(x[:-4])
+            self.render('init.htm',db=db)
+            return
+        tb = self.application.db['params']
+        if tb.find_one({'app':'bot'}):
+            tb.update({'app':'bot'}, {'app':'bot', 'default':de+'_bot'})
+        else:
+            tb.insert({'app':'bot', 'default':de+'_bot'})
         for x in glob.glob('./*.txt'):
             f = open(x)
             data = f.read()
             f.close()
-            self.main(x[2:-4], data)
+            self.main(x[2:-4].lower(), data)
     
     def main(self, name, data):
         if name == 'requirements':
@@ -668,7 +711,7 @@ class TokenHandler(web.RequestHandler):
             table.insert(data)
         self.finish()
 
-    @web.asynchronous   
+    #@web.asynchronous
     def get(self):
         url = 'https://api.line.me/v2/oauth/accessToken'
         headers = 'application/x-www-form-urlencoded'
@@ -677,30 +720,22 @@ class TokenHandler(web.RequestHandler):
         req = httpclient.HTTPRequest(url=url,method='POST',headers=headers,body=body)
         http = httpclient.AsyncHTTPClient()
         http.fetch(req, callback=self.on_response)
-    
-class TestHandler(web.RequestHandler):
-    text = escape.json_encode({'type':'text', 'text':'hello'})
-    def get(self):
-        self.write(self.text)
-        
-    def post(self):
-        self.write(self.text)
-    
-class Application(web.Application):  
-    id = os.environ['Bot_Id']  
+
+class Application(web.Application):
     ch = os.environ['Channel_Secret']
     uri = os.environ['MONGODB_URI']
-    ac = os.environ['ACCOUNT']   
+    ac = os.environ['ACCOUNT']
+    tk = os.environ['Access_Token']
     db = pymongo.MongoClient(uri)[ac]
     def __init__(self):
         handlers = [(r'/',NaviHandler),(r'/login',LoginHandler),(r'/logout',LogoutHandler),(r'/title',TitleHandler),
-                    (r'/headline/api',HeadlineApi),(r'/read/api/([a-zA-Z0-9_]+)/([0-9]+)',ArticleApi),
-                    (r'/write/api/([a-zA-Z0-9_]+)/()/()/()',ArticleApi),(r'/list/api/([a-zA-Z0-9]+)',ListApi),
+                    (r'/headline/api',HeadlineApi),(r'/read/api/([a-zA-Z0-9_%]+)/([0-9]+)',ArticleApi),
+                    (r'/write/api/([a-zA-Z0-9_%]+)/()/()/()',ArticleApi),(r'/list/api/([a-zA-Z0-9_%]+)',ListApi),
                     (r'/help',HelpHandler),(r'/master',MasterHandler),(r'/alert',AlertHandler),(r'/jump',JumpHandler),
-                    (r'/callback',WebHookHandler),(r'/init',InitHandler),(r'/search',SearchHandler),(r'/clean',CleanHandler),(r'/hatena',TestHandler),(r'/token',TokenHandler),
-                    (r'/([a-zA-Z0-9_]+)',IndexHandler),(r'/([a-zA-Z0-9_]+)/([0-9]+)/',IndexHandler),
-                    (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),
-                    (r'/([a-zA-Z0-9_]+)/search',SearchHandler),(r'/([a-zA-Z0-9_]+)/regist',RegistHandler)]
+                    (r'/callback',WebHookHandler),(r'/init',InitHandler),(r'/search',SearchHandler),(r'/clean',CleanHandler),(r'/token',TokenHandler),
+                    (r'/([a-zA-Z0-9_%]+)',IndexHandler),(r'/([a-zA-Z0-9_%]+)/([0-9]+)/',IndexHandler),
+                    (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),
+                    (r'/([a-zA-Z0-9_%]+)/search',SearchHandler),(r'/([a-zA-Z0-9_%]+)/regist',RegistHandler)]
         settings = {'template_path':os.path.join(os.path.dirname(__file__),'templates'),
                         'static_path':os.path.join(os.path.dirname(__file__),'static'),
                         'ui_modules':{'Footer':FooterModule},
@@ -719,19 +754,29 @@ class Application(web.Application):
         elif (pos-1)*params['count'] >= self.db[dbname].count():
             pos = 0
         return pos
-    
-    def collection(self,name):
-        if name in self.db.collection_names():
-            return True
+
+    def page(self,dbname,number):
+        table = self.db[dbname]
+        rec = table.find({'number':{'$lte':int(number)}}).count()
+        s = self.db['params'].find_one({'app':'bbs'})
+        conf = int(s['count'])
+        if table.find().count() - rec >= conf:
+            return '/'+dbname+'/'+str(1+rec//conf)+'/#'+number
         else:
-            return False
+            return '/'+dbname+'#'+number
 
-    def coll(self):
-        name = self.db.collection_names()
-        for x in ['objectlabs-system.admin.collections','objectlabs-system','system.indexes',
-                  'params','master','temp']:
-            if x in name:
-                name.remove(x)
+    def mylist(self):
+        return self.db.list_collection_names()[:]
+
+    def coll(self,info=False):
+        name = self.mylist()
+        item = self.db['params'].find_one({'app':'bbs'})
+        target = ['objectlabs-system', 'objectlabs-system.admin.collections', 'system.indexes',
+            'params', 'master', 'temp']
+        if info is False:
+            target.append(item['info name'])
+        for x in target:
+            name.remove(x)
         for x in name:
             if x[-4:] == '_bot':
                 name.remove(x)