Parcourir la source

Merge pull request #906 from 2du/master

stable
잉여개발기 (SPDV) il y a 6 ans
Parent
commit
396ba894af
60 fichiers modifiés avec 1484 ajouts et 1185 suppressions
  1. 41 22
      app.py
  2. 38 13
      emergency_tool.py
  3. 12 3
      language/en-US.json
  4. 38 30
      language/ko-KR.json
  5. 7 3
      readme.md
  6. 1 0
      requirements.txt
  7. 6 5
      route/api_search.py
  8. 5 1
      route/api_skin_info.py
  9. 14 0
      route/api_title_index.py
  10. 8 18
      route/api_topic_sub.py
  11. 5 1
      route/api_version.py
  12. 1 1
      route/application_submitted.py
  13. 6 2
      route/edit.py
  14. 1 1
      route/edit_delete.py
  15. 1 1
      route/edit_many_delete.py
  16. 2 2
      route/edit_move.py
  17. 3 3
      route/edit_req.py
  18. 1 1
      route/edit_revert.py
  19. 71 59
      route/func_upload.py
  20. 4 3
      route/give_acl.py
  21. 7 0
      route/inter_wiki.py
  22. 2 0
      route/inter_wiki_del.py
  23. 11 0
      route/inter_wiki_plus.py
  24. 9 17
      route/list_acl.py
  25. 2 3
      route/list_not_close_topic.py
  26. 17 32
      route/list_user_topic.py
  27. 1 1
      route/login.py
  28. 1 1
      route/login_register.py
  29. 3 2
      route/main_manager.py
  30. 18 25
      route/recent_changes.py
  31. 3 10
      route/recent_discuss.py
  32. 4 4
      route/search_deep.py
  33. 26 11
      route/setting.py
  34. 273 197
      route/tool/func.py
  35. 6 11
      route/tool/mark.py
  36. 272 240
      route/tool/set_mark/namumark.py
  37. 1 4
      route/tool/set_mark/tool.py
  38. 27 28
      route/topic.py
  39. 70 0
      route/topic_acl.py
  40. 10 11
      route/topic_admin.py
  41. 8 9
      route/topic_block.py
  42. 61 0
      route/topic_change.py
  43. 18 28
      route/topic_close_list.py
  44. 4 6
      route/topic_delete.py
  45. 27 39
      route/topic_stop.py
  46. 15 9
      route/topic_tool.py
  47. 9 11
      route/topic_top.py
  48. 7 7
      route/user_count_edit.py
  49. 15 17
      route/view_raw.py
  50. 21 21
      route/view_read.py
  51. 6 6
      version.json
  52. 1 1
      views/main_css/css/main.css
  53. 24 0
      views/main_css/js/load_namumark.js
  54. 232 0
      views/main_css/js/load_skin_set.js
  55. 3 14
      views/main_css/js/load_topic.js
  56. 0 10
      views/main_css/js/req_browser_alarm.js
  57. 1 1
      views/marisa/index.html
  58. 2 2
      views/marisa/info.json
  59. 0 49
      views/marisa/js/search.js
  60. 2 189
      views/marisa/js/skin_set.js

+ 41 - 22
app.py

@@ -8,19 +8,18 @@ for i_data in os.listdir("route"):
 
         exec("from route." + f_src + " import *")
 
-version_list = json.loads(open('version.json').read())
+# DB
+version_list = json.loads(open('version.json', encoding='utf8').read())
+app_var = json.loads(open('data/app_var.json', encoding='utf8').read())
 
 print('Version : ' + version_list['master']['r_ver'])
 print('DB set version : ' + version_list['master']['c_ver'])
 print('Skin set version : ' + version_list['master']['s_ver'])
 print('----')
 
-app_var = json.loads(open('data/app_var.json').read())
-
-# DB
 while 1:
     try:
-        set_data = json.loads(open('data/set.json').read())
+        set_data = json.loads(open('data/set.json', encoding='utf8').read())
         if not 'db_type' in set_data:
             try:
                 os.remove('data/set.json')
@@ -68,10 +67,10 @@ while 1:
             if new_json[1] == '':
                 new_json[1] = 'data'
 
-            with open('data/set.json', 'w') as f:
+            with open('data/set.json', 'w', encoding='utf8') as f:
                 f.write('{ "db" : "' + new_json[1] + '", "db_type" : "' + new_json[0] + '" }')
 
-            set_data = json.loads(open('data/set.json').read())
+            set_data = json.loads(open('data/set.json', encoding='utf8').read())
 
             break
 
@@ -79,12 +78,12 @@ db_data_get(set_data['db_type'])
 
 if set_data['db_type'] == 'mysql':
     try:
-        set_data_mysql = json.loads(open('data/mysql.json').read())
+        set_data_mysql = json.loads(open('data/mysql.json', encoding='utf8').read())
     except:
         new_json = ['', '']
 
         while 1:
-            print('DB user id : ', end = '')
+            print('DB user ID : ', end = '')
             new_json[0] = str(input())
             if new_json[0] != '':
                 break
@@ -94,14 +93,19 @@ if set_data['db_type'] == 'mysql':
             new_json[1] = str(input())
             if new_json[1] != '':
                 break
+                
+        print('DB host (localhost) : ', end = '')
+        new_json[2] = str(input())
+        if new_json[2] == '':
+            new_json[2] == 'localhost'
 
-        with open('data/mysql.json', 'w') as f:
-            f.write('{ "user" : "' + new_json[0] + '", "password" : "' + new_json[1] + '" }')
+        with open('data/mysql.json', 'w', encoding='utf8') as f:
+            f.write('{ "user" : "' + new_json[0] + '", "password" : "' + new_json[1] + '", "host" : "' + new_json[2] + '" }')
 
-        set_data_mysql = json.loads(open('data/mysql.json').read())
+        set_data_mysql = json.loads(open('data/mysql.json', encoding='utf8').read())
 
     conn = pymysql.connect(
-        host = 'localhost',
+        host = set_data_mysql['host'] if 'host' in set_data_mysql else 'localhost',
         user = set_data_mysql['user'],
         password = set_data_mysql['password'],
         charset = 'utf8mb4'
@@ -171,12 +175,12 @@ if setup_tool != 0:
     create_data['data'] = ['title', 'data']
     create_data['cache_data'] = ['title', 'data', 'id']
     create_data['history'] = ['id', 'title', 'data', 'date', 'ip', 'send', 'leng', 'hide', 'type']
-    create_data['rd'] = ['title', 'sub', 'date', 'band', 'stop', 'agree']
+    create_data['rd'] = ['title', 'sub', 'code', 'date', 'band', 'stop', 'agree', 'acl']
     create_data['user'] = ['id', 'pw', 'acl', 'date', 'encode']
     create_data['user_set'] = ['name', 'id', 'data']
     create_data['user_application'] = ['id', 'pw', 'date', 'encode', 'question', 'answer', 'ip', 'ua', 'token', 'email']
     create_data['ban'] = ['block', 'end', 'why', 'band', 'login']
-    create_data['topic'] = ['id', 'title', 'sub', 'data', 'date', 'ip', 'block', 'top', 'code']
+    create_data['topic'] = ['id', 'data', 'date', 'ip', 'block', 'top', 'code']
     create_data['rb'] = ['block', 'end', 'today', 'blocker', 'why', 'band']
     create_data['back'] = ['title', 'link', 'type']
     create_data['custom'] = ['user', 'css']
@@ -205,7 +209,7 @@ if setup_tool != 0:
                 pass
 
     if setup_tool == 1:
-        update(int(ver_set_data[0][0]))
+        update(int(ver_set_data[0][0]), set_data)
     else:
         set_init()
 
@@ -310,7 +314,7 @@ if set_data['db_type'] == 'sqlite':
 if set_data['db_type'] == 'mysql':
     def mysql_dont_off():
         try:
-            urllib.request.urlopen('http://localhost:' + str(server_set['port']) + '/')
+            urllib.request.urlopen('http://localhost:' + server_set['port'] + '/')
         except:
             pass
 
@@ -325,6 +329,8 @@ if not curs.fetchall():
 
 conn.commit()
 
+print('Now running... http://localhost:' + server_set['port'])
+
 if os.path.exists('custom.py'):
     from custom import custom_run
 
@@ -339,16 +345,16 @@ def alarm_del():
 def alarm():
     return alarm_2(conn)
 
-@app.route('/<regex("inter_wiki|edit_top|image_license|(?:edit|email|file|name)_filter"):tools>')
+@app.route('/<regex("inter_wiki|edit_top|image_license|(?:edit|email|file|name|extension)_filter"):tools>')
 def inter_wiki(tools = None):
     return inter_wiki_2(conn, tools)
 
-@app.route('/<regex("del_(?:inter_wiki|edit_top|image_license|(?:edit|email|file|name)_filter)"):tools>/<name>')
+@app.route('/<regex("del_(?:inter_wiki|edit_top|image_license|(?:edit|email|file|name|extension)_filter)"):tools>/<name>')
 def inter_wiki_del(tools = None, name = None):
     return inter_wiki_del_2(conn, tools, name)
 
-@app.route('/<regex("plus_(?:inter_wiki|edit_top|image_license|(?:edit|email|file|name)_filter)"):tools>', methods=['POST', 'GET'])
-@app.route('/<regex("plus_(?:inter_wiki|edit_top|image_license|(?:edit|email|file|name|edit)_filter)"):tools>/<name>', methods=['POST', 'GET'])
+@app.route('/<regex("plus_(?:inter_wiki|edit_top|image_license|(?:edit|email|file|name|extension)_filter)"):tools>', methods=['POST', 'GET'])
+@app.route('/<regex("plus_(?:inter_wiki|edit_top|image_license|(?:edit|email|file|name|extension)_filter)"):tools>/<name>', methods=['POST', 'GET'])
 def inter_wiki_plus(tools = None, name = None):
     return inter_wiki_plus_2(conn, tools, name)
 
@@ -497,6 +503,10 @@ def topic_top(topic_num = 1, num = 1):
 def topic_stop(topic_num = 1):
     return topic_stop_2(conn, topic_num)
 
+@app.route('/thread/<int:topic_num>/acl', methods=['POST', 'GET'])
+def topic_acl(topic_num = 1):
+    return topic_acl_2(conn, topic_num)
+
 @app.route('/thread/<int:topic_num>/delete', methods=['POST', 'GET'])
 def topic_delete(topic_num = 1):
     return topic_delete_2(conn, topic_num)
@@ -505,6 +515,10 @@ def topic_delete(topic_num = 1):
 def topic_tool(topic_num = 1):
     return topic_tool_2(conn, topic_num)
 
+@app.route('/thread/<int:topic_num>/change', methods=['POST', 'GET'])
+def topic_change(topic_num = 1):
+    return topic_change_2(conn, topic_num)
+
 @app.route('/thread/<int:topic_num>/admin/<int:num>')
 def topic_admin(topic_num = 1, num = 1):
     return topic_admin_2(conn, topic_num, num)
@@ -634,6 +648,7 @@ def main_image_view(name = None):
     return main_image_view_2(conn, name, app_var)
 
 @app.route('/skin_set')
+@app.route('/main_skin_set')
 def main_skin_set():
     return main_skin_set_2(conn)
 
@@ -687,6 +702,10 @@ def api_recent_change():
 def api_sha224(name = 'test'):
     return api_sha224_2(conn, name)
 
+@app.route('/api/title_index')
+def api_title_index():
+    return api_title_index_2(conn)
+
 @app.route('/api/image/<name>')
 def api_image_view(name = ''):
     return api_image_view_2(conn, name, app_var)
@@ -712,7 +731,7 @@ app.debug = True
 if __name__ == "__main__":
     try:
         http_server = tornado.httpserver.HTTPServer(tornado.wsgi.WSGIContainer(app))
-        http_server.listen(server_set['port'], address = server_set['host'])
+        http_server.listen(int(server_set['port']), address = server_set['host'])
 
         tornado.ioloop.IOLoop.instance().start()
     except Exception as e:

+ 38 - 13
emergency_tool.py

@@ -1,9 +1,16 @@
 import time
 from route.tool.func import *
 
-version_list = json.loads(open('version.json', encoding='utf8').read())
 
 # DB
+version_list = json.loads(open('version.json', encoding='utf8').read())
+app_var = json.loads(open('data/app_var.json', encoding='utf8').read())
+
+print('Version : ' + version_list['master']['r_ver'])
+print('DB set version : ' + version_list['master']['c_ver'])
+print('Skin set version : ' + version_list['master']['s_ver'])
+print('----')
+
 while 1:
     try:
         set_data = json.loads(open('data/set.json', encoding='utf8').read())
@@ -70,7 +77,7 @@ if set_data['db_type'] == 'mysql':
         new_json = ['', '']
 
         while 1:
-            print('DB user id : ', end = '')
+            print('DB user ID : ', end = '')
             new_json[0] = str(input())
             if new_json[0] != '':
                 break
@@ -80,14 +87,19 @@ if set_data['db_type'] == 'mysql':
             new_json[1] = str(input())
             if new_json[1] != '':
                 break
+                
+        print('DB host (localhost) : ', end = '')
+        new_json[2] = str(input())
+        if new_json[2] == '':
+            new_json[2] == 'localhost'
 
         with open('data/mysql.json', 'w', encoding='utf8') as f:
-            f.write('{ "user" : "' + new_json[0] + '", "password" : "' + new_json[1] + '" }')
+            f.write('{ "user" : "' + new_json[0] + '", "password" : "' + new_json[1] + '", "host" : "' + new_json[2] + '" }')
 
         set_data_mysql = json.loads(open('data/mysql.json', encoding='utf8').read())
 
     conn = pymysql.connect(
-        host = 'localhost',
+        host = set_data_mysql['host'] if 'host' in set_data_mysql else 'localhost',
         user = set_data_mysql['user'],
         password = set_data_mysql['password'],
         charset = 'utf8mb4'
@@ -146,23 +158,23 @@ try:
     curs.execute(db_change('select data from other where name = "ver"'))
     ver_set_data = curs.fetchall()
     if not ver_set_data:
-        setup_tool = 1
+        setup_tool = 2
     else:
         if int(version_list['master']['c_ver']) > int(ver_set_data[0][0]):
             setup_tool = 1
 except:
-    setup_tool = 1
+    setup_tool = 2
 
 if setup_tool != 0:
     create_data['data'] = ['title', 'data']
-    create_data['cache_data'] = ['title', 'data']
+    create_data['cache_data'] = ['title', 'data', 'id']
     create_data['history'] = ['id', 'title', 'data', 'date', 'ip', 'send', 'leng', 'hide', 'type']
-    create_data['rd'] = ['title', 'sub', 'date', 'band', 'stop', 'agree']
+    create_data['rd'] = ['title', 'sub', 'code', 'date', 'band', 'stop', 'agree', 'acl']
     create_data['user'] = ['id', 'pw', 'acl', 'date', 'encode']
     create_data['user_set'] = ['name', 'id', 'data']
     create_data['user_application'] = ['id', 'pw', 'date', 'encode', 'question', 'answer', 'ip', 'ua', 'token', 'email']
     create_data['ban'] = ['block', 'end', 'why', 'band', 'login']
-    create_data['topic'] = ['id', 'title', 'sub', 'data', 'date', 'ip', 'block', 'top', 'code']
+    create_data['topic'] = ['id', 'data', 'date', 'ip', 'block', 'top', 'code']
     create_data['rb'] = ['block', 'end', 'today', 'blocker', 'why', 'band']
     create_data['back'] = ['title', 'link', 'type']
     create_data['custom'] = ['user', 'css']
@@ -190,7 +202,10 @@ if setup_tool != 0:
             except:
                 pass
 
-    update()
+    if setup_tool == 1:
+        update(int(ver_set_data[0][0]), set_data)
+    else:
+        set_init()
 
 curs.execute(db_change('delete from other where name = "ver"'))
 curs.execute(db_change('insert into other (name, data) values ("ver", ?)'), [version_list['master']['c_ver']])
@@ -205,11 +220,12 @@ print('4. Change host')
 print('5. Change port')
 print('6. Change skin')
 print('7. Change password')
-print('8. Reset version')
+print('8. Change version')
 print('9. Delete set.json')
 print('10. Change name')
 print('11. Delete mysql.json')
 print('12. All title count reset')
+print('13. Cache data reset')
 
 print('----')
 print('Select : ', end = '')
@@ -321,7 +337,14 @@ elif what_i_do == '7':
 
     curs.execute(db_change("update user set pw = ? where id = ?"), [hashed, user_name])
 elif what_i_do == '8':
-    curs.execute(db_change("update other set data = '00000' where name = 'ver'"))
+    print('----')
+    print('Insert version (0000000) : ', end = '')
+    new_ver = input()
+    
+    if new_ver == '':
+        new_ver == '0000000'
+
+    curs.execute(db_change("update other set data = ? where name = 'ver'"), [new_ver])
 elif what_i_do == '9':
     try:
         os.remove('data/set.json')
@@ -342,7 +365,7 @@ elif what_i_do == '11':
         os.remove('data/mysql.json')
     except:
         pass
-else:
+elif what_i_do == '12':
     curs.execute(db_change("select count(title) from data"))
     count_data = curs.fetchall()
     if count_data:
@@ -352,6 +375,8 @@ else:
 
     curs.execute(db_change('delete from other where name = "count_all_title"'))
     curs.execute(db_change('insert into other (name, data) values ("count_all_title", ?)'), [str(count_data)])
+else:
+    curs.execute(db_change('delete from cache_data'))
 
 conn.commit()
 

+ 12 - 3
language/en-US.json

@@ -98,7 +98,7 @@
         "content" : "Content",
         "off" : "Off",
         "unset" : "Unset",
-        "delete_admin_group": "Delete admin group",
+        "extension" : "Extension",
         "_comment_1.1_" : "Time",
             "second" : "Second(s)",
             "hour" : "Hour(s)",
@@ -195,6 +195,13 @@
         "add_watchlist" : "Add watchlist",
         "blocked_user" : "Blocked user",
         "blocked_admin" : "Blocked administrator",
+        "category_title" : "Documents under this category",
+        "ban_acl" : "Include blocked users",
+        "topic_name_change" : "Change discussion name",
+        "topic_acl_setting" : "Discussion ACL setting",
+        "topic_acl" : "Discussion ACL",
+        "delete_admin_group": "Delete admin group",
+        "main_skin_set" : "Main skin setting(s)",
         "_comment_2.1_" : "Filter",
             "_comment_2.1.1_" : "List",
                 "interwiki_list" : "Interwiki(s) list",
@@ -204,6 +211,7 @@
                 "file_filter_list" : "File name filter(s) list",
                 "edit_tool_list" : "Edit tool(s) list",
                 "image_license_list" : "Image license(s) list",
+                "extension_filter_list" : "Extension filter(s) list",
             "_comment_2.1.2_" : "Add",
                 "interwiki_add" : "Add Interwiki",
                 "edit_filter_add" : "Add contents filter",
@@ -212,6 +220,7 @@
                 "file_filter_add" : "Add file name filter",
                 "edit_tool_add" : "Add edit tool",
                 "image_license_add" : "Add image license",
+                "extension_filter_add" : "Add extension filter",
         "_comment_2.2_" : "Setting",
             "setting" : "Setting",
             "restart_required" : "Restart required",
@@ -309,7 +318,7 @@
             "owner_acl" : "Owner only",
             "_comment_2.6_1_" : "Set",
                 "document_acl" : "Document ACL",
-                "discussion_acl" : "Discussion ACL",
+                "discussion_acl" : "Discussion(s) ACL",
                 "view_acl" : "Reading ACL",
                 "user_document_acl" : "User document ACL",
                 "upload_acl" : "Upload ACL",
@@ -355,7 +364,7 @@
             "file_exist_error" : "The file does not exist.",
             "password_error" : "The password is different.",
             "recaptcha_error" : "Go through the reCAPTCHA.",
-            "file_extension_error" : "Only JPG, GIF, JPEG, PNG, WEBP files are allowed.",
+            "file_extension_error" : "Only files with the specified extension can be uploaded.",
             "edit_record_error" : "Edit reason can not be more than 500 characters.",
             "same_file_error" : "A file with the same name exists.",
             "file_capacity_error" : "Maximum file capacity (MB): ",

+ 38 - 30
language/ko-KR.json

@@ -3,7 +3,7 @@
     "document_name": "문서명",
     "non_login_alert": "비로그인 알림",
     "hide": "숨김",
-    "discussion_acl": "토론 ACL",
+    "discussion_acl": "토론 ACL",
     "ongoing": "진행 중",
     "user_tool": "사용자 도구",
     "compare_target": "비교 대상 이름",
@@ -15,23 +15,23 @@
     "move_history": "이동 기록",
     "register": "회원가입",
     "reset_user_text": "암호 초기화 완료 문구",
-    "no_register": "가입 불가",
+    "no_register": "가입 불가",
     "view_acl": "읽기 ACL",
     "notice": "알림",
     "login": "로그인",
     "smtp_setting": "이메일 SMTP 설정",
-    "smtp_server" : "SMTP 서버 주소",
+    "smtp_server": "SMTP 서버 주소",
     "smtp_security": "SMTP 보안 프로토콜",
-    "smtp_port" : "SMTP 서버 포트",
+    "smtp_port": "SMTP 서버 포트",
     "close": "닫기",
     "closed": "닫힘",
     "start": "시작",
     "ban_release": "차단 해제",
-    "encryption_method": "암호화 방식",
+    "encryption_method": "비밀번호 암호화 방식",
     "why": "사유",
     "user_check_authority": "사용자 검사 권한",
     "second": "초",
-    "load": "다른 문서 불러오기",
+    "load": "불러오기",
     "user_discussion": "사용자 토론",
     "upper": "상위",
     "email": "이메일",
@@ -46,7 +46,7 @@
     "state": "상태",
     "authorize": "권한 부여",
     "check_user": "사용자 검사",
-    "email_acl": "이메일을 가진 사용자만",
+    "email_acl": "이메일 인증을 받은 사용자만",
     "email_error": "해당 이메일을 가진 사용자가 존재하지 않습니다.",
     "version": "버전",
     "open": "열기",
@@ -88,7 +88,7 @@
     "file_name_error": "파일 이름에는 알파벳, 한글, 공백, 밑줄 및 빼기 기호만 사용할 수 있습니다.",
     "pass": "넘기기",
     "recaptcha_error": "'로봇이 아닙니다'를 통해 reCAPTCHA를 통과하세요.",
-    "file_capacity_error": "최대 파일 크기 (MB) : ",
+    "file_capacity_error": "최대 파일 크기 (MB): ",
     "setting": "설정",
     "end": "끝",
     "error": "오류",
@@ -114,7 +114,7 @@
     "topic_tool": "토론 관리 도구",
     "user_document": "사용자 문서",
     "id": "아이디",
-    "no_login_warring": "비로그인 상태입니다. 편집시 지금 접속한 IP가 기록됩니다.",
+    "no_login_warring": "비로그인 상태입니다. 편집 시 지금 접속한 IP가 기록됩니다.",
     "decument_404_error": "이 문서는 존재하지 않습니다.",
     "key": "키",
     "lastest": "최신",
@@ -146,7 +146,7 @@
     "pinned": "고정",
     "edit_filter_add": "편집 필터 추가",
     "ban_authority": "차단 권한",
-    "file_extension_error": "오직 JPG, GIF, JPEG, PNG, WEBP 형식의 파일만 업로드할 수 있습니다.",
+    "file_extension_error": "지정된 확장자의 파일만 업로드 할 수 있습니다.",
     "host": "호스트",
     "email_text": "이메일 내용",
     "recent": "최근",
@@ -167,7 +167,7 @@
     "skin_error": "사용 중인 스킨은 스킨 설정 기능을 지원하지 않습니다.",
     "member": "가입자",
     "backlink": "역링크",
-    "no_admin_block_error": "관리자는 검사, 차단을 할 수 없습니다.",
+    "no_admin_block_error": "관리자를 검사하거나 차단할 수 없습니다.",
     "recaptcha": "reCAPTCHA",
     "create": "생성",
     "restart": "재시작",
@@ -317,7 +317,7 @@
     "sqlite_only": "SQLite만",
     "off": "끄기",
     "slow_edit": "편집 속도 제한 시간",
-    "requires_approval" : "가입시 승인 필요",
+    "requires_approval": "가입시 승인 필요",
     "approval_question": "회원가입 질문",
     "public_key": "공개 키",
     "fast_edit_error": "편집 속도가 너무 빠릅니다. 제한 (초): ",
@@ -329,25 +329,33 @@
     "approve": "승인",
     "decline": "거절",
     "approve_or_decline": "승인 및 거절",
-    "history_add" : "역사 추가",
-    "too_many_dec_error" : "문서 수가 너무 많아서 지원하지 않는 기능입니다.",
-    "approval_question_visible_only_when_approval_on" : "회원가입 질문은 가입시 승인필요 설정이 활성화됐을때만 보여집니다.",
-    "no_applications_now" : "회원가입 신청이 없습니다.",
-    "application_not_found" : "존재하지 않는 회원가입 신청입니다.",
+    "history_add": "역사 추가",
+    "too_many_dec_error": "문서 수가 너무 많아서 지원하지 않는 기능입니다.",
+    "approval_question_visible_only_when_approval_on": "회원가입 질문은 가입시 승인필요 설정이 활성화됐을때만 보여집니다. 이 설정은 메인 설정에서 활성하실 수 있습니다.",
+    "no_applications_now": "회원가입 신청이 없습니다.",
+    "application_not_found": "존재하지 않는 회원가입 신청입니다.",
     "approval_requirement_disabled": "현재 가입시 승인필요 설정이 비활성화되어있습니다. 필요시 설정에서 활성화할 수 있습니다.",
-    "all_register_num" : "모든 가입 신청자의 수",
-    "replace_move" : "문서 바꾸기",
-    "merge_move" : "문서 병합",
-    "oauth_explain" : "OAuth 로그인 기능을 사용하려면 'publish_url' 값을 HTTPS 프로토콜을 포함한 도메인 주소로 설정하고, 실제로 HTTPS 연결을 지원해야 합니다.",
-    "add_admin_group" : "관리자 그룹 추가",
-    "add_watchlist" : "주시 문서 추가",
-    "blocked_user" : "차단된 사용자",
-    "blocked_admin" : "차단한 관리자",
-    "invalid_password_error" : "비밀번호 또는 아이디가 없습니다.",
+    "all_register_num": "모든 가입 신청자의 수",
+    "replace_move": "문서 바꾸기",
+    "merge_move": "문서 병합",
+    "oauth_explain": "OAuth 로그인 기능을 사용하려면 'publish_url' 값을 HTTPS 프로토콜을 포함한 도메인 주소로 설정하고, 실제로 HTTPS 연결을 지원해야 합니다.",
+    "add_admin_group": "관리자 그룹 추가",
+    "add_watchlist": "주시 문서 추가",
+    "blocked_user": "차단된 사용자",
+    "blocked_admin": "차단한 관리자",
+    "invalid_password_error": "비밀번호 또는 아이디가 없습니다.",
     "accept_edit_request": "편집 요청 승인",
     "msg_whatchlist_lmt": "다음 개수만큼 추가할 수 있습니다",
-    "watchlist_overflow_error": "추가 한도를 초과했으므로 더 이상 추가할 수 없습니다. 필요없는 항목을 삭재하십시오.",
-    "unset" : "미설정",
-    "copyright_disagreed" : "저작권 동의문구에 체크하여야 합니다."
-
+    "watchlist_overflow_error": "추가 한도를 초과했으므로 더 이상 추가할 수 없습니다. 필요없는 항목을 삭제하십시오.",
+    "unset": "미설정",
+    "copyright_disagreed": "저작권 동의문구에 동의하여야 합니다.",
+    "category_title": "이 분류 아래 문서들",
+    "extension_filter_list": "확장자 필터 목록",
+    "extension_filter_add": "확장자 필터 추가",
+    "extension": "확장자",
+    "ban_acl": "차단된 사용자 포함",
+    "topic_name_change": "토론 제목 변경",
+    "topic_acl_setting" : "토론 ACL 설정",
+    "topic_acl" : "토론 ACL",
+    "main_skin_set" : "기본 스킨 설정"
 }

+ 7 - 3
readme.md

@@ -18,14 +18,14 @@
 ## 시작하기
 오픈나무는 파이썬 환경에서 동작하는 파이썬 애플리케이션으로, 파이썬 환경을 필요로 합니다.
 
-쉬운 오픈나무 설치를 위해 오픈나무 가이드를 따로 생성해두었으며, [이곳](https://github.com/openNAMU/openNAMU-Guide)에서 확인하실 수 있습니다.
+쉬운 오픈나무 설치를 위해 오픈나무 가이드를 따로 생성해두었으며, [이곳](http://2du.pythonanywhere.com)에서 확인하실 수 있습니다.
 
 ## 클론
 아래 명령을 터미널(명령 프롬프트)에 입력하여 본 리포지토리를 클론할 수 있습니다.
 ### 일반
  * `git clone -b stable https://github.com/2du/openNAMU.git`
 
-### 개발
+### 개발
  * `git clone -b master https://github.com/2du/openNAMU.git`
 
 ## 기여
@@ -42,6 +42,10 @@
  * Numerical expression - [MathJax](https://www.mathjax.org/)
  * Handling Keyboard Shortcuts [shortcut.js](http://www.openjs.com/scripts/events/keyboard_shortcuts/)
 
+## 지원 문법
+ * 나무마크 (NamuMark)
+ * 마크다운 (Markdown) (예정)
+
 ## 기여자 목록
  * [참고](https://github.com/2DU/openNAMU/graphs/contributors)
 
@@ -53,4 +57,4 @@
 
 ## 기타
  * 첫 가입자에게 소유자 권한이 부여됩니다.
- * [테스트 서버](http://2du.pythonanywhere.com)
+ * [테스트 서버](http://2du.pythonanywhere.com)

+ 1 - 0
requirements.txt

@@ -3,6 +3,7 @@ bcrypt
 flask
 flask-Reggie
 flask-compress
+requests
 pymysql
 diff-match-patch
 pysha3; python_version < "3.6"

+ 6 - 5
route/api_search.py

@@ -19,14 +19,15 @@ def api_search_2(conn, name):
     curs.execute(db_change('select data from other where name = "count_all_title"'))
     if int(curs.fetchall()[0][0]) < 30000:
         curs.execute(db_change("" + \
-            "select distinct title, case when title like ? then 'title' else 'data' " + \
-            "end from data where title like ? or data like ? order by case " + \
-            "when title like ? then 1 else 2 end limit ?, ?"),
+            "select distinct title, case " + \
+            "when title >= '' and title like ? then 'title' else 'data' end from data " + \
+            "where title >= '' and (title like ? or data like ?) order by case " + \
+            "when title >= '' and title like ? then 1 else 2 end limit ?, ?"),
             ['%' + name + '%', '%' + name + '%', '%' + name + '%', '%' + name + '%', page, num]
         )
     else:
-        curs.execute(db_change("select title from data where title like ? order by title limit ?, 50"),
-            ['%' + name + '%', sql_num]
+        curs.execute(db_change("select title from data where title >= '' and title like ? order by title limit ?, ?"),
+            ['%' + name + '%', page, num]
         )
     all_list = curs.fetchall()
     if all_list:

+ 5 - 1
route/api_skin_info.py

@@ -48,7 +48,11 @@ def api_skin_info_2(conn, name):
                     get_num = 0
 
                 if get_num == 1:
-                    get_data = urllib.request.urlopen(info_link)
+                    try:
+                        get_data = urllib.request.urlopen(info_link)
+                    except:
+                        get_data = None
+
                     if get_data and get_data.getcode() == 200:
                         try:
                             get_data = json.loads(get_data.read().decode())

+ 14 - 0
route/api_title_index.py

@@ -0,0 +1,14 @@
+from .tool.func import *
+
+def api_title_index_2(conn):
+    curs = conn.cursor()
+
+    if flask.request.args.get('count', '1') == '1':
+        curs.execute(db_change('select data from other where name = "count_all_title"'))
+        title_count = curs.fetchall()
+        if title_count:
+            return flask.jsonify({ 'count' : title_count[0][0] })
+        else:
+            return flask.jsonify({ 'count' : '0' })
+    else:
+        return flask.jsonify({})

+ 8 - 18
route/api_topic_sub.py

@@ -3,20 +3,17 @@ from .tool.func import *
 def api_topic_sub_2(conn, topic_num):
     curs = conn.cursor()
 
-    topic_change_data = topic_change(topic_num)
-    name = topic_change_data[0]
-    sub = topic_change_data[1]
+    topic_num = str(topic_num)
 
     if flask.request.args.get('num', None):
-        curs.execute(db_change("select id, data, date, ip, block, top from topic where title = ? and sub = ? and id + 0 = ? + 0 order by id + 0 asc"), [
-            name,
-            sub,
+        curs.execute(db_change("select id, data, date, ip, block, top from topic where code = ? and id + 0 = ? + 0 order by id + 0 asc"), [
+            topic_num,
             flask.request.args.get('num', '')
         ])
     elif flask.request.args.get('top', None):
-        curs.execute(db_change("select id, data, date, ip, block, top from topic where title = ? and sub = ? and top = 'O' order by id + 0 asc"), [name, sub])
+        curs.execute(db_change("select id, data, date, ip, block, top from topic where code = ? and top = 'O' order by id + 0 asc"), [topic_num])
     else:
-        curs.execute(db_change("select id, data, date, ip, block, top from topic where title = ? and sub = ? order by id + 0 asc"), [name, sub])
+        curs.execute(db_change("select id, data, date, ip, block, top from topic where code = ? order by id + 0 asc"), [topic_num])
 
     data = curs.fetchall()
     if data:
@@ -37,21 +34,14 @@ def api_topic_sub_2(conn, topic_num):
                     t_data_f = ''
                     b_color = 'toron_color_not'
 
-                curs.execute(db_change("select who from re_admin where what = ? order by time desc limit 1"), [
-                    'blind (' + name + ' - ' + sub + '#' + str(i[0]) + ')'
-                ])
-                who_blind = curs.fetchall()
-                if who_blind:
-                    ip += ' (' + who_blind[0][0] + ' B)'
-                else:
-                    ip += ' (B)'
+                ip += ' (B)'
 
             if flask.request.args.get('render', None):
                 if i[0] == '1':
                     s_user = i[3]
                 else:
                     if flask.request.args.get('num', None):
-                        curs.execute(db_change("select ip from topic where title = ? and sub = ? order by id + 0 asc limit 1"), [name, sub])
+                        curs.execute(db_change("select ip from topic where code = ? order by id + 0 asc limit 1"), [topic_num])
                         g_data = curs.fetchall()
                         if g_data:
                             s_user = g_data[0][0]
@@ -68,7 +58,7 @@ def api_topic_sub_2(conn, topic_num):
                     t_color = 'toron_color'
 
                 if admin == 1 or b_color != 'toron_color_not':
-                    ip += ' <a href="/thread/' + str(topic_num) + '/admin/' + i[0] + '">(' + load_lang('discussion_tool') + ')</a>'
+                    ip += ' <a href="/thread/' + topic_num + '/admin/' + i[0] + '">(' + load_lang('discussion_tool') + ')</a>'
 
                 if t_data_f == '':
                     t_data_f = '[br]'

+ 5 - 1
route/api_version.py

@@ -13,7 +13,11 @@ def api_version_2(conn, r_ver, c_ver):
     else:
         up_data = 'stable'
 
-    data = urllib.request.urlopen('https://raw.githubusercontent.com/2du/openNAMU/master/version.json')
+    try:
+        data = urllib.request.urlopen('https://raw.githubusercontent.com/2du/openNAMU/master/version.json')
+    except:
+        data = None
+
     if data and data.getcode() == 200:
         try:
             json_data = json.loads(data.read().decode())

+ 1 - 1
route/application_submitted.py

@@ -5,6 +5,6 @@ def application_submitted_2(conn):
 
     return easy_minify(flask.render_template(skin_check(),
         imp = [load_lang('application_submitted'), wiki_set(), custom(), other2([0, 0])],
-        data =  '''<p>''' + load_lang('waiting_for_approval') + '''</p>''',
+        data = '<p>' + load_lang('waiting_for_approval') + '</p>',
         menu = [['user', load_lang('return')]]
     ))

+ 6 - 2
route/edit.py

@@ -13,7 +13,7 @@ def edit_2(conn, name):
         return redirect('/edit_req/' + url_pas(name))
     
     if flask.request.method == 'POST':
-        if captcha_post(flask.request.form.get('g-recaptcha-response', '')) == 1:
+        if captcha_post(flask.request.form.get('g-recaptcha-response', flask.request.form.get('g-recaptcha', ''))) == 1:
             return re_error('/error/13')
         else:
             captcha_post('', 0)
@@ -129,7 +129,11 @@ def edit_2(conn, name):
                 data = get_data[0][0]
 
         save_button = load_lang('save')
-        menu_plus = [['delete/' + url_pas(name), load_lang('delete')], ['move/' + url_pas(name), load_lang('move')]]
+        menu_plus = [
+            ['delete/' + url_pas(name), load_lang('delete')], 
+            ['move/' + url_pas(name), load_lang('move')], 
+            ['upload', load_lang('upload')]
+        ]
         sub = load_lang('edit')
 
         curs.execute(db_change('select data from other where name = "edit_bottom_text"'))

+ 1 - 1
route/edit_delete.py

@@ -8,7 +8,7 @@ def edit_delete_2(conn, name, app_var):
         return re_error('/ban')
 
     if flask.request.method == 'POST':
-        if captcha_post(flask.request.form.get('g-recaptcha-response', '')) == 1:
+        if captcha_post(flask.request.form.get('g-recaptcha-response', flask.request.form.get('g-recaptcha', ''))) == 1:
             return re_error('/error/13')
         else:
             captcha_post('', 0)

+ 1 - 1
route/edit_many_delete.py

@@ -8,7 +8,7 @@ def edit_many_delete_2(conn, app_var):
         return re_error('/ban')
 
     if flask.request.method == 'POST':
-        all_title = re.findall(r'([^\n]+)\n', flask.request.form.get('content', '').replace('\r\n', '\n') + '\n')
+        all_title = re.findall('([^\n]+)\n', flask.request.form.get('content', '').replace('\r\n', '\n') + '\n')
         for name in all_title:
             curs.execute(db_change("select data from data where title = ?"), [name])
             data = curs.fetchall()

+ 2 - 2
route/edit_move.py

@@ -7,7 +7,7 @@ def edit_move_2(conn, name):
         return re_error('/ban')
 
     if flask.request.method == 'POST':
-        if captcha_post(flask.request.form.get('g-recaptcha-response', '')) == 1:
+        if captcha_post(flask.request.form.get('g-recaptcha-response', flask.request.form.get('g-recaptcha', ''))) == 1:
             return re_error('/error/13')
         else:
             captcha_post('', 0)
@@ -43,7 +43,7 @@ def edit_move_2(conn, name):
                     ip_check(),
                     flask.request.form.get('send', ''),
                     '0',
-                    'marge <a>' + name + '</a> - <a>' + move_title + '</a> move'
+                    'merge <a>' + name + '</a> - <a>' + move_title + '</a> move'
                 )
 
                 curs.execute(db_change("update back set type = 'no' where title = ? and not type = 'cat' and not type = 'no'"), [name])

+ 3 - 3
route/edit_req.py

@@ -11,7 +11,7 @@ def edit_req_2(conn, name):
         section = flask.request.args.get('section', None)
 
     if acl_check(name) == 1:
-        if acl_check(name, 'edit_req') == 1 or re.search('^user:', name) or ban_check() == 1 or get_ver:
+        if acl_check(name, 'edit_req') == 1 or re.search('^user:', name) or get_ver:
             return re_error('/ban')
     else:
         if not get_ver:
@@ -23,7 +23,7 @@ def edit_req_2(conn, name):
         curs.execute(db_change("select data from data where title = ?"), [name])
         old = curs.fetchall()
         if not old:
-            return redirect('/w/' + url_pas(name))
+            return redirect('/ban')
     else:
         curs.execute(db_change("select data, send, ip, date from history where title = ? and id = ? and type = 'req'"), [name, str(get_ver)])
         old = curs.fetchall()
@@ -31,7 +31,7 @@ def edit_req_2(conn, name):
             return redirect('/w/' + url_pas(name))
 
     if flask.request.method == 'POST':
-        if captcha_post(flask.request.form.get('g-recaptcha-response', '')) == 1:
+        if captcha_post(flask.request.form.get('g-recaptcha-response', flask.request.form.get('g-recaptcha', ''))) == 1:
             return re_error('/error/13')
         else:
             captcha_post('', 0)

+ 1 - 1
route/edit_revert.py

@@ -13,7 +13,7 @@ def edit_revert_2(conn, name):
         return re_error('/ban')
 
     if flask.request.method == 'POST':
-        if captcha_post(flask.request.form.get('g-recaptcha-response', '')) == 1:
+        if captcha_post(flask.request.form.get('g-recaptcha-response', flask.request.form.get('g-recaptcha', ''))) == 1:
             return re_error('/error/13')
         else:
             captcha_post('', 0)

+ 71 - 59
route/func_upload.py

@@ -7,86 +7,98 @@ def func_upload_2(conn):
         return re_error('/ban')
 
     if flask.request.method == 'POST':
-        if captcha_post(flask.request.form.get('g-recaptcha-response', '')) == 1:
+        if captcha_post(flask.request.form.get('g-recaptcha-response', flask.request.form.get('g-recaptcha', ''))) == 1:
             return re_error('/error/13')
         else:
             captcha_post('', 0)
 
-        data = flask.request.files.get('f_data', None)
-        if not data:
+        file_data = flask.request.files.getlist("f_data[]", None)
+        if not file_data:
             return re_error('/error/9')
 
-        if int(wiki_set(3)) * 1024 * 1024 < flask.request.content_length:
-            return re_error('/error/17')
+        if len(file_data) == 1:
+            file_num = None
+        else:
+            file_num = 1
 
-        value = os.path.splitext(data.filename)[1]
-        if not value in ['.jpeg', '.jpg', '.gif', '.png', '.webp', '.JPEG', '.JPG', '.GIF', '.PNG', '.WEBP']:
-            return re_error('/error/14')
+        for data in file_data:
+            if int(wiki_set(3)) * 1024 * 1024 < flask.request.content_length:
+                return re_error('/error/17')
 
-        if flask.request.form.get('f_name', None):
-            name = flask.request.form.get('f_name', None) + value
-        else:
-            name = data.filename
+            value = os.path.splitext(data.filename)[1]
+            
+            curs.execute(db_change("select html from html_filter where kind = 'extension'"))
+            extension = [i[0].lower() for i in curs.fetchall()]
+            if not re.sub('^\.', '', value).lower() in extension:
+                return re_error('/error/14')
+
+            if flask.request.form.get('f_name', None):
+                name = flask.request.form.get('f_name', None) + (' ' + str(file_num) if file_num else '') + value
+            else:
+                name = data.filename
 
-        piece = os.path.splitext(name)
-        if re.search('[^ㄱ-힣0-9a-zA-Z_\- ]', piece[0]):
-            return re_error('/error/22')
+            piece = os.path.splitext(name)
+            if re.search('[^ㄱ-힣0-9a-zA-Z_\- ]', piece[0]):
+                return re_error('/error/22')
 
-        e_data = sha224_replace(piece[0]) + piece[1]
+            e_data = sha224_replace(piece[0]) + piece[1]
 
-        curs.execute(db_change("select title from data where title = ?"), ['file:' + name])
-        if curs.fetchall():
-            return re_error('/error/16')
+            curs.execute(db_change("select title from data where title = ?"), ['file:' + name])
+            if curs.fetchall():
+                return re_error('/error/16')
 
-        curs.execute(db_change("select html from html_filter where kind = 'file'"))
-        db_data = curs.fetchall()
-        for i in db_data:
-            t_re = re.compile(i[0])
-            if t_re.search(name):
-                return redirect('/file_filter')
+            curs.execute(db_change("select html from html_filter where kind = 'file'"))
+            db_data = curs.fetchall()
+            for i in db_data:
+                t_re = re.compile(i[0])
+                if t_re.search(name):
+                    return redirect('/file_filter')
 
-        ip = ip_check()
+            ip = ip_check()
 
-        if flask.request.form.get('f_lice_sel', 'direct_input') == 'direct_input':
-            lice = flask.request.form.get('f_lice', None) + '[br][br]'
-            if ip_or_user(ip) != 0:
-                lice += ip
+            if flask.request.form.get('f_lice_sel', 'direct_input') == 'direct_input':
+                lice = flask.request.form.get('f_lice', '') + '[br][br]'
+                if ip_or_user(ip) != 0:
+                    lice += ip
+                else:
+                    lice += '[[user:' + ip + ']]'
+
+                lice += '[[category:direct_input]]'
             else:
-                lice += '[[user:' + ip + ']]'
+                lice = flask.request.form.get('f_lice_sel', '')
+                lice += '[br][br]'  + flask.request.form.get('f_lice', '')
+                lice += '[[category:' + re.sub('\]', '_', flask.request.form.get('f_lice_sel', '')) + ']]'
 
-            lice += '[[category:direct_input]]'
-        else:
-            lice = flask.request.form.get('f_lice_sel', None)
-            lice += '[br][br]'  + flask.request.form.get('f_lice', None)
-            lice += '[[category:' + re.sub('\]', '_', flask.request.form.get('f_lice_sel', None)) + ']]'
+            if os.path.exists(os.path.join(app_var['path_data_image'], e_data)):
 
-        if os.path.exists(os.path.join(app_var['path_data_image'], e_data)):
-            os.remove(os.path.join(app_var['path_data_image'], e_data))
+                os.remove(os.path.join(app_var['path_data_image'], e_data))
 
-            data.save(os.path.join(app_var['path_data_image'], e_data))
-        else:
-            data.save(os.path.join(app_var['path_data_image'], e_data))
+                data.save(os.path.join(app_var['path_data_image'], e_data))
+            else:
+                data.save(os.path.join(app_var['path_data_image'], e_data))
+
+            file_d = '[[file:' + name + ']][br][br]{{{[[file:' + name + ']]}}}[br][br]' + lice
 
-        file_d = '[[file:' + name + ']][br][br]{{{[[file:' + name + ']]}}}[br][br]' + lice
+            curs.execute(db_change("insert into data (title, data) values (?, ?)"), ['file:' + name, file_d])
+            curs.execute(db_change("insert into acl (title, decu, dis, why, view) values (?, 'admin', '', '', '')"), ['file:' + name])
 
-        curs.execute(db_change("insert into data (title, data) values (?, ?)"), ['file:' + name, file_d])
-        curs.execute(db_change("insert into acl (title, decu, dis, why, view) values (?, 'admin', '', '', '')"), ['file:' + name])
+            render_set(
+                title = 'file:' + name,
+                data = file_d,
+                num = 1
+            )
 
-        render_set(
-            title = 'file:' + name,
-            data = file_d,
-            num = 1
-        )
+            history_plus(
+                'file:' + name,
+                file_d,
+                get_time(),
+                ip,
+                ip,
+                '0',
+                'upload'
+            )
 
-        history_plus(
-            'file:' + name,
-            file_d,
-            get_time(),
-            ip,
-            ip,
-            '0',
-            'upload'
-        )
+            if file_num: file_num += 1
 
         conn.commit()
 
@@ -111,7 +123,7 @@ def func_upload_2(conn):
                 ''' + load_lang('max_file_size') + ''' : ''' + wiki_set(3) + '''MB
                 <hr class=\"main_hr\">
                 <form method="post" enctype="multipart/form-data" accept-charset="utf8">
-                    <input type="file" name="f_data">
+                    <input multiple="multiple" type="file" name="f_data[]">
                     <hr class=\"main_hr\">
                     <input placeholder="''' + load_lang('file_name') + '''" name="f_name" value="''' + flask.request.args.get('name', '') + '''">
                     <hr class=\"main_hr\">

+ 4 - 3
route/give_acl.py

@@ -69,9 +69,9 @@ def give_acl_2(conn, name):
         data = '<h2>' + load_lang('document_acl') + '</h2><hr class=\"main_hr\"><select name="decu" ' + check_ok + '>'
 
         if re.search('^user:', name):
-            acl_list = ['', 'user', 'all']
+            acl_list = get_acl_list('user')
         else:
-            acl_list = ['', 'all', 'user', 'admin', 'owner', '50_edit', 'email']
+            acl_list = get_acl_list()
 
         curs.execute(db_change("select decu from acl where title = ?"), [name])
         acl_data = curs.fetchall()
@@ -112,7 +112,7 @@ def give_acl_2(conn, name):
             data += '''
                 </select>
                 <hr class=\"main_hr\">
-                <h2>''' + load_lang('explanation') + '''</h2>
+                <h2 id="exp">''' + load_lang('explanation') + '''</h2>
                 <ul>
                     <li>normal : ''' + load_lang('unset') + '''</li>
                     <li>admin : ''' + load_lang('admin_acl') + '''</li>
@@ -121,6 +121,7 @@ def give_acl_2(conn, name):
                     <li>all : ''' + load_lang('all_acl') + '''</li>
                     <li>email : ''' + load_lang('email_acl') + '''</li>
                     <li>owner : ''' + load_lang('owner_acl') + '''</li>
+                    <li>ban : ''' + load_lang('ban_acl') + '''</li>
                 </ul>
             '''
 

+ 7 - 0
route/inter_wiki.py

@@ -55,6 +55,13 @@ def inter_wiki_2(conn, tools):
         div = ''
 
         curs.execute(db_change("select html from html_filter where kind = 'image_license'"))
+    elif tools == 'extension_filter':
+        del_link = 'del_extension_filter'
+        plus_link = 'plus_extension_filter'
+        title = load_lang('extension_filter_list')
+        div = ''
+
+        curs.execute(db_change("select html from html_filter where kind = 'extension'"))
     else:
         del_link = 'del_edit_top'
         plus_link = 'plus_edit_top'

+ 2 - 0
route/inter_wiki_del.py

@@ -16,6 +16,8 @@ def inter_wiki_del_2(conn, tools, name):
             curs.execute(db_change("delete from html_filter where html = ? and kind = 'email'"), [name])
         elif tools == 'del_image_license':
             curs.execute(db_change("delete from html_filter where html = ? and kind = 'image_license'"), [name])
+        elif tools == 'del_extension_filter':
+            curs.execute(db_change("delete from html_filter where html = ? and kind = 'extension'"), [name])
         else:
             curs.execute(db_change("delete from html_filter where html = ? and kind = 'edit_top'"), [name])
 

+ 11 - 0
route/inter_wiki_plus.py

@@ -64,6 +64,10 @@ def inter_wiki_plus_2(conn, tools, name):
                 admin_check(None, 'image_license edit')
 
                 type_d = 'image_license'
+            elif tools == 'plus_extension_filter':
+                admin_check(None, 'extension_filter edit')
+
+                type_d = 'extension'
             else:
                 admin_check(None, 'edit_top edit')
 
@@ -181,6 +185,13 @@ def inter_wiki_plus_2(conn, tools, name):
                 '<hr class=\"main_hr\">' + \
                 '<input value="' + (name if name else '') + '" type="text" name="title">' + \
             ''
+        elif tools == 'plus_extension_filter':
+            title = load_lang('extension_filter_add')
+            form_data = '' + \
+                load_lang('extension') + \
+                '<hr class=\"main_hr\">' + \
+                '<input value="' + (name if name else '') + '" type="text" name="title">' + \
+            ''
         else:
             title = load_lang('edit_tool_add')
             if name:

+ 9 - 17
route/list_acl.py

@@ -5,35 +5,27 @@ def list_acl_2(conn):
 
     div = '<ul>'
 
-    curs.execute(db_change("select title, decu, dis, view, why from acl where decu != '' or dis != '' or view != '' order by title desc"))
+    curs.execute(db_change("select title, why from acl where decu != '' or dis != '' or view != '' order by title desc"))
     list_data = curs.fetchall()
     for data in list_data:
         if not re.search('^user:', data[0]) and not re.search('^file:', data[0]):
-            acl = []
-            for i in range(1, 4):
-                if data[i] == 'admin':
-                    acl += [load_lang('admin')]
-                elif data[i] == 'user':
-                    acl += [load_lang('member')]
-                elif data[i] == '':
-                    acl += [load_lang('normal')]
-                else:
-                    acl += [data[i]]
-
             curs.execute(db_change("select time from re_admin where what like ? order by time desc limit 1"), ['acl (' + data[0] + ')%'])
             time_data = curs.fetchall()
             if time_data:
-                time_data = ' | ' + time_data[0][0]
+                time_data = time_data[0][0] + ' | '
             else:
                 time_data = ''
+                
+            if data[1] != '':
+                why = ' | ' + data[1]
+            else:
+                why = ''
 
             div += '' + \
                 '<li>' + \
-                    '<a href="/w/' + url_pas(data[0]) + '">' + data[0] + '</a> | ' + \
-                    load_lang('document_acl') + ' : ' + acl[0] + ' | ' + \
-                    load_lang('discussion_acl') + ' : ' + acl[1] + ' | ' + \
-                    load_lang('view_acl') + ' : ' + acl[2] + \
                     time_data + \
+                    '<a href="/acl/' + url_pas(data[0]) + '">' + data[0] + '</a>' + \
+                     why + \
                 '</li>' + \
             ''
 

+ 2 - 3
route/list_not_close_topic.py

@@ -5,11 +5,10 @@ def list_not_close_topic_2(conn):
 
     div = '<ul>'
 
-    curs.execute(db_change('select title, sub, date from rd where stop != "O" order by date desc'))
+    curs.execute(db_change('select title, sub, date, code from rd where stop != "O" order by date desc'))
     n_list = curs.fetchall()
     for data in n_list:
-        curs.execute(db_change("select code from topic where id = '1' and title = ? and sub = ?"), [data[0], data[1]])
-        div += '<li><a href="/thread/' + url_pas(curs.fetchall()[0][0]) + '">' + html.escape(data[0]) + '</a> (' + data[1] + ') | ' + data[2] + '</li>'
+        div += '<li>' + data[2] + ' | <a href="/thread/' + data[3] + '">' + html.escape(data[1]) + '</a> (' + html.escape(data[0]) + ')</li>'
 
     div += '</ul>'
 

+ 17 - 32
route/list_user_topic.py

@@ -9,46 +9,31 @@ def list_user_topic_2(conn, name):
     else:
         sql_num = 0
 
-    one_admin = admin_check(1)
-
-    div =   '''
-            <table id="main_table_set">
-                <tbody>
-                    <tr>
-                        <td id="main_table_width">''' + load_lang('discussion_name') + '''</td>
-                        <td id="main_table_width">''' + load_lang('writer') + '''</td>
-                        <td id="main_table_width">''' + load_lang('time') + '''</td>
-                    </tr>
-            '''
-
-    curs.execute(db_change("select title, id, sub, ip, date from topic where ip = ? order by date desc limit ?, 50"), [name, sql_num])
+    div = '''
+        <table id="main_table_set">
+            <tbody>
+                <tr>
+                    <td id="main_table_width">''' + load_lang('discussion_name') + '''</td>
+                    <td id="main_table_width">''' + load_lang('writer') + '''</td>
+                    <td id="main_table_width">''' + load_lang('time') + '''</td>
+                </tr>
+    '''
+
+    curs.execute(db_change("select code, id, ip, date from topic where ip = ? order by date desc limit ?, 50"), [name, sql_num])
     data_list = curs.fetchall()
     for data in data_list:
         title = html.escape(data[0])
         sub = html.escape(data[2])
 
-        if one_admin == 1:
-            curs.execute(db_change("select * from ban where block = ?"), [data[3]])
-            if curs.fetchall():
-                ban = ' <a href="/ban/' + url_pas(data[3]) + '">(' + load_lang('release') + ')</a>'
-            else:
-                ban = ' <a href="/ban/' + url_pas(data[3]) + '">(' + load_lang('ban') + ')</a>'
-        else:
-            ban = ''
-
-        curs.execute(db_change("select code from topic where id = '1' and title = ? and sub = ?"), [title, sub])
-        div += '<tr><td><a href="/thread/' + url_pas(curs.fetchall()[0][0]) + '#' + data[1] + '">' + title + '#' + data[1] + '</a> (' + sub + ')</td>'
-        div += '<td>' + ip_pas(data[3]) + ban + '</td><td>' + data[4] + '</td></tr>'
+        curs.execute(db_change("select title, sub from rd where code = ?"), [data[0]])
+        other_data = curs.fetchall()
+        
+        div += '<tr><td><a href="/thread/' + data[0] + '#' + data[1] + '">' + other_data[0][1] + '#' + data[1] + '</a> (' + other_data[0][0] + ')</td>'
+        div += '<td>' + ip_pas(data[2]) + '</td><td>' + data[3] + '</td></tr>'
 
     div += '</tbody></table>'
     div += next_fix('/topic_record/' + url_pas(name) + '?num=', num, data_list)
-
-    curs.execute(db_change("select end from ban where block = ?"), [name])
-    if curs.fetchall():
-        sub = ' (' + load_lang('blocked') + ')'
-    else:
-        sub = 0
-
+    
     return easy_minify(flask.render_template(skin_check(),
         imp = [load_lang('discussion_record'), wiki_set(), custom(), other2([sub, 0])],
         data = div,

+ 1 - 1
route/login.py

@@ -11,7 +11,7 @@ def login_2(conn):
         return re_error('/ban')
 
     if flask.request.method == 'POST':
-        if captcha_post(flask.request.form.get('g-recaptcha-response', '')) == 1:
+        if captcha_post(flask.request.form.get('g-recaptcha-response', flask.request.form.get('g-recaptcha', ''))) == 1:
             return re_error('/error/13')
         else:
             captcha_post('', 0)

+ 1 - 1
route/login_register.py

@@ -17,7 +17,7 @@ def login_register_2(conn):
             return re_error('/ban')
 
     if flask.request.method == 'POST':
-        if captcha_post(flask.request.form.get('g-recaptcha-response', '')) == 1:
+        if captcha_post(flask.request.form.get('g-recaptcha-response', flask.request.form.get('g-recaptcha', ''))) == 1:
             return re_error('/error/13')
         else:
             captcha_post('', 0)

+ 3 - 2
route/main_manager.py

@@ -17,7 +17,7 @@ def main_manager_2(conn, num, r_ver):
         10 : [0, 'block_admin', load_lang('blocked_admin')],
         11 : [load_lang('document_name'), 'watch_list', load_lang('add_watchlist')],
         12 : [load_lang('compare_target'), 'check', load_lang('compare_target')],
-        13 : [load_lang('document_name'), 'edit', load_lang('edit')]
+        13 : [load_lang('document_name'), 'edit', load_lang('load')]
     }
 
     if num == 1:
@@ -37,6 +37,7 @@ def main_manager_2(conn, num, r_ver):
                         <li><a href="/give_log">''' + load_lang('admin_group_list') + '''</a></li>
                         <li><a href="/many_delete">''' + load_lang('many_delete') + '''</a></li>
                         <li><a href="/applications">''' + load_lang('application_list') + '''</a></li>
+                        <li><a href="/adsense_setting">''' + load_lang('adsense_setting') + '''</a></li>
                         <li><a href="/setting">''' + load_lang('setting') + '''</a></li>
                     </ul>
                     <h3>''' + load_lang('filter') + '''</h3>
@@ -48,13 +49,13 @@ def main_manager_2(conn, num, r_ver):
                         <li><a href="/email_filter">''' + load_lang('email_filter_list') + '''</a></li>
                         <li><a href="/name_filter">''' + load_lang('id_filter_list') + '''</a></li>
                         <li><a href="/file_filter">''' + load_lang('file_filter_list') + '''</a></li>
+                        <li><a href="/extension_filter">''' + load_lang('extension_filter_list') + '''</a></li>
                     </ul>
                     <br>
                     <h2>''' + load_lang('server') + '''</h2>
                     <ul>
                         <li><a href="/restart">''' + load_lang('wiki_restart') + '''</a></li>
                         <li><a href="/update">''' + load_lang('update') + '''</a></li>
-                        <li><a href="/adsense_setting">''' + load_lang('adsense_setting') + '''</a></li>
                     </ul>
                     <br>
                     <h2>''' + load_lang('version') + '''</h2>

+ 18 - 25
route/recent_changes.py

@@ -38,15 +38,15 @@ def recent_changes_2(conn, name, tool):
 
                 tool_select = flask.request.args.get('tool', 'normal')
                 if tool_select == 'move':
-                    plus_sql = 'where (send like ? or send like ?) and type = "" '
+                    plus_sql = 'where send >= "" and (send like ? or send like ?) and type = "" '
                     plus_list = ['%(<a>' + name +'</a>%', '%<a>' + name + '</a> move)', sql_num]
                     sub += ' (' + load_lang('move') + ')'
                 elif tool_select == 'delete':
-                    plus_sql = 'where (send like "%(delete)") and title = ? and type = "" '
+                    plus_sql = 'where send like "%(delete)" and title = ? and type = "" '
                     plus_list = [name, sql_num]
                     sub += ' (' + load_lang('delete') + ')'
                 elif tool_select == 'revert':
-                    plus_sql = 'where (send like ?) and title = ? and type = "" '
+                    plus_sql = 'where send >= "" and send like ? and title = ? and type = "" '
                     plus_list = ['%(r%)', name, sql_num]
                     sub += ' (' + load_lang('revert') + ')'
                 else:
@@ -54,7 +54,7 @@ def recent_changes_2(conn, name, tool):
                     plus_list = [name, sql_num]
 
                 curs.execute(db_change('' + \
-                    'select id, title, date, ip, send, leng from history ' + \
+                    'select id, title, date, ip, send, leng, hide from history ' + \
                     plus_sql + \
                     'order by id + 0 desc ' + \
                     "limit ?, 50" + \
@@ -69,7 +69,7 @@ def recent_changes_2(conn, name, tool):
                 div = '<a href="/topic_record/' + url_pas(name) + '">(' + load_lang('discussion') + ')</a><hr class=\"main_hr\">' + div
 
                 curs.execute(db_change('' + \
-                    'select id, title, date, ip, send, leng from history ' + \
+                    'select id, title, date, ip, send, leng, hide from history ' + \
                     "where ip = ? and type = '' order by date desc limit ?, 50" + \
                 ''), [name, sql_num])
         else:
@@ -95,7 +95,7 @@ def recent_changes_2(conn, name, tool):
                 plus_sql = "where not title like 'user:%' and type = '' "
 
             curs.execute(db_change('' + \
-                'select id, title, date, ip, send, leng from history ' + \
+                'select id, title, date, ip, send, leng, hide from history ' + \
                 plus_sql + \
                 'order by date desc ' + \
                 'limit ?, 50' + \
@@ -128,29 +128,22 @@ def recent_changes_2(conn, name, tool):
             style = ['', '']
             date = data[2]
 
-            curs.execute(db_change('''
-                select title from history
-                where title = ? and id = ? and hide = 'O'
-            '''), [data[1], data[0]])
-            hide = curs.fetchall()
-
-            if admin_check(6) == 1:
-                if hide:
+            print(data)
+            if data[6] == 'O':
+                if admin_check(6) == 1:
                     style[0] = 'id="toron_color_grey"'
                     style[1] = 'id="toron_color_grey"'
 
                     send += ' (' + load_lang('hide') + ')'
-            elif not hide:
-                pass
-            else:
-                ip = ''
-                ban = ''
-                date = ''
+                else:
+                    ip = ''
+                    ban = ''
+                    date = ''
 
-                send = '(' + load_lang('hide') + ')'
+                    send = '(' + load_lang('hide') + ')'
 
-                style[0] = 'style="display: none;"'
-                style[1] = 'id="toron_color_grey"'
+                    style[0] = 'style="display: none;"'
+                    style[1] = 'id="toron_color_grey"'
 
             if tool == 'history':
                 title = '<a href="/w/' + url_pas(name) + '?num=' + data[0] + '">r' + data[0] + '</a> '
@@ -161,7 +154,7 @@ def recent_changes_2(conn, name, tool):
                     title = '<a href="/w/' + url_pas(data[1]) + '">' + html.escape(data[1]) + '</a> '
                     title += '<a href="/history/' + url_pas(data[1]) + '">(r' + data[0] + ')</a> '
 
-            div +=  '''
+            div += '''
                 <tr ''' + style[0] + '''>
                     <td>''' + title + m_tool + ' ' + leng + '''</td>
                     <td>''' + ip + ban + '''</td>
@@ -172,7 +165,7 @@ def recent_changes_2(conn, name, tool):
                 </tr>
             '''
 
-        div +=  '''
+        div += '''
                 </tbody>
             </table>
         '''

+ 3 - 10
route/recent_discuss.py

@@ -25,22 +25,15 @@ def recent_discuss_2(conn):
             '''
 
     if m_sub == 0:
-        curs.execute(db_change("select title, sub, date from rd where not stop = 'O' order by date desc limit 50"))
+        curs.execute(db_change("select title, sub, date, code from rd where not stop = 'O' order by date desc limit 50"))
     else:
-        curs.execute(db_change("select title, sub, date from rd where stop = 'O' order by date desc limit 50"))
+        curs.execute(db_change("select title, sub, date, code from rd where stop = 'O' order by date desc limit 50"))
 
     for data in curs.fetchall():
-        curs.execute(db_change("select code from topic where id = '1' and title = ? and sub = ?"), [data[0], data[1]])
-        get_code = curs.fetchall()
-        if get_code and get_code[0][0] != '':
-            get_code = get_code[0][0]
-        else:
-            get_code = '1'
-
         title = html.escape(data[0])
         sub = html.escape(data[1])
 
-        div += '<tr><td><a href="/thread/' + get_code + '">' + sub + '</a> (' + title + ')</td><td>' + data[2] + '</td></tr>'
+        div += '<tr><td><a href="/thread/' + data[3] + '">' + sub + '</a> (' + title + ')</td><td>' + data[2] + '</td></tr>'
 
     div += '</tbody></table>'
 

+ 4 - 4
route/search_deep.py

@@ -37,9 +37,9 @@ def search_deep_2(conn, name):
     if int(curs.fetchall()[0][0]) < 30000:
         curs.execute(db_change("" + \
             "select distinct title, case " + \
-            "when title like ? then 'title' else 'data' end from data " + \
-            "where title like ? or data like ? order by case " + \
-            "when title like ? then 1 else 2 end limit ?, 50"),
+            "when title >= '' and title like ? then 'title' else 'data' end from data " + \
+            "where title >= '' and (title like ? or data like ?) order by case " + \
+            "when title >= '' and title like ? then 1 else 2 end limit ?, 50"),
             ['%' + name + '%', '%' + name + '%', '%' + name + '%', '%' + name + '%', sql_num]
         )
         all_list = curs.fetchall()
@@ -54,7 +54,7 @@ def search_deep_2(conn, name):
 
                 div_plus += '<li><a href="/w/' + url_pas(data[0]) + '">' + data[0] + '</a> (' + data[1] + ')</li>'
     else:
-        curs.execute(db_change("select title from data where title like ? order by title limit ?, 50"),
+        curs.execute(db_change("select title from data where title >= '' and title like ? order by title limit ?, 50"),
             ['%' + name + '%', sql_num]
         )
         all_list = curs.fetchall()

+ 26 - 11
route/setting.py

@@ -293,7 +293,7 @@ def setting_2(conn, num):
                         <hr class=\"main_hr\">
                         <input name="''' + i_list[11] + '''" value="''' + html.escape(d_list[11]) + '''">
                         <hr>
-                        <span>''' + load_lang('approval_question') + '''</span> <a href="#rfn-1" id="fn-1">(1)</a>
+                        <span>''' + load_lang('approval_question') + '''</span><sup><a href="#rfn-1" id="fn-1">(1)</a></sup>
                         <hr class=\"main_hr\">
                         <input name="''' + i_list[12] + '''" value="''' + html.escape(d_list[12]) + '''">
                         <hr class=\"main_hr\">
@@ -465,7 +465,8 @@ def setting_2(conn, num):
             'smtp_port',
             'smtp_security',
             'smtp_email',
-            'smtp_pass'
+            'smtp_pass',
+            'recaptcha_ver'
         ]
 
         if flask.request.method == 'POST':
@@ -502,19 +503,29 @@ def setting_2(conn, num):
             for i in ['tls', 'starttls', 'plain']:
                 security_radios += '<input name="smtp_security" type="radio" value="' + i + '" ' + ('checked' if d_list[4] == i else '') + '>' + i + '<hr class="main_hr">'
 
+            re_ver = ''
+            if d_list[7] == '':
+                re_ver += '<option value="">v2</option><option value="v3">v3</option>'
+            else:
+                re_ver += '<option value="v3">v3</option><option value="">v2</option>'
+
             return easy_minify(flask.render_template(skin_check(),
                 imp = ['Google', wiki_set(), custom(), other2([0, 0])],
                 data = '''
                     <form method="post">
                         <h2><a href="https://www.google.com/recaptcha/admin">''' + load_lang('recaptcha') + '''</a></h2>
-                        <span>HTML</span>
+                        <span>''' + load_lang('public_key') + '''</span>
+                        <hr class=\"main_hr\">
+                        <input name="recaptcha" value="''' + html.escape(d_list[0]) + '''">
                         <hr class=\"main_hr\">
-                        <input name="recaptcha" placeholder='&lt;div class="g-recaptcha" data-sitekey="''' + load_lang('public_key') + '''"&gt;&lt;/div&gt;' value="''' + html.escape(d_list[0]) + '''">
-                        <hr>
                         <span>''' + load_lang('secret_key') + '''</span>
                         <hr class=\"main_hr\">
                         <input name="sec_re" value="''' + html.escape(d_list[1]) + '''">
                         <hr class=\"main_hr\">
+                        <select name="recaptcha_ver">
+                            ''' + re_ver + '''
+                        </select>
+                        <hr class=\"main_hr\">
                         <h2>''' + load_lang('smtp_setting') + ' (' + load_lang('restart_required') + ''')</h1>
                         <span>''' + load_lang('smtp_server') + '''</span>
                         <hr class=\"main_hr\">
@@ -585,19 +596,23 @@ def setting_2(conn, num):
             conn.commit()
 
             acl_div = ['', '', '', '', '']
-            acl_list = ['normal', 'user', 'admin', 'owner', '50_edit', 'email']
+            acl_list = get_acl_list()
             for i in range(0, 5):
-                for acl_data in acl_list:
-                    if acl_data == d_list[i + 1]:
-                        acl_div[i] = '<option value="' + acl_data + '">' + acl_data + '</option>' + acl_div[i]
+                for data_list in acl_list:
+                    if data_list == d_list[i + 1]:
+                        check = 'selected="selected"'
                     else:
-                        acl_div[i] += '<option value="' + acl_data + '">' + acl_data + '</option>'
+                        check = ''
+                    
+                    acl_div[i] += '<option value="' + data_list + '" ' + check + '>' + (data_list if data_list != '' else 'normal') + '</option>'
 
             return easy_minify(flask.render_template(skin_check(),
                 imp = [load_lang('main_acl_setting'), wiki_set(), custom(), other2([0, 0])],
                 data = '''
                     <form method="post">
-                        <span>''' + load_lang('document_acl') + '</span> <a href="/acl/TEST">(' + load_lang('reference') + ''')</a>
+                        <a href="/acl/TEST#exp">(''' + load_lang('reference') + ''')</a>
+                        <hr>
+                        <span>''' + load_lang('document_acl') + '''</span> 
                         <hr class=\"main_hr\">
                         <select name="edit">''' + acl_div[0] + '''</select>
                         <hr>

+ 273 - 197
route/tool/func.py

@@ -166,47 +166,49 @@ def render_set(title = '', data = '', num = 0, s_data = 0, include = None):
         else:
             return 'HTTP Request 404'
 
-def captcha_get():
-    data = ''
-
-    if ip_or_user() != 0:
-        curs.execute(db_change('select data from other where name = "recaptcha"'))
-        recaptcha = curs.fetchall()
-        if recaptcha and recaptcha[0][0] != '':
-            curs.execute(db_change('select data from other where name = "sec_re"'))
-            sec_re = curs.fetchall()
-            if sec_re and sec_re[0][0] != '':
-                data += '<script src="https://www.google.com/recaptcha/api.js" async defer></script>' + recaptcha[0][0] + '<hr class=\"main_hr\">'
-
-    return data
-
-def update(ver_num):
+def update(ver_num, set_data):
     print('----')
     # 업데이트 하위 호환 유지 함수
-    # v3.1.5
-    try:
-        num = 1
-        curs.execute(db_change('select title, sub from topic where id = "1" order by date asc'))
-        db_data = curs.fetchall()
-        if db_data:
-            for i in db_data:
-                curs.execute(db_change("update topic set code = ? where title = ? and sub = ? and id = '1'"), [str(num), i[0], i[1]])
-                num += 1
-
-            print('Add topic code')
-    except:
-        pass
 
     if ver_num < 3160027:
         print('Add init set')
         set_init()
 
-    if ver_num < 3160500:
+    if ver_num < 3170002:
+        curs.execute(db_change("select html from html_filter where kind = 'extension'"))
+        if not curs.fetchall():
+            for i in ['jpg', 'jpeg', 'png', 'gif', 'webp']:
+                curs.execute(db_change("insert into html_filter (html, kind) values (?, 'extension')"), [i])
+
+    if ver_num < 3170400:
+        curs.execute(db_change("select title, sub, code from topic where id = '1'"))
+        change_topic = curs.fetchall()
+        for i in change_topic:
+            curs.execute(db_change("update topic set code = ? where title = ? and sub = ?"), [i[2], i[0], i[1]])
+            curs.execute(db_change("update rd set code = ? where title = ? and sub = ?"), [i[2], i[0], i[1]])
+
+    if ver_num < 3171600:
         curs.execute(db_change('delete from cache_data'))
 
+    if ver_num < 3171800:
+        curs.execute(db_change("select data from other where name = 'recaptcha'"))
+        change_rec = curs.fetchall()
+        if change_rec and change_rec[0][0] != '':
+            new_rec = re.search('data-sitekey="([^"]+)"', change_rec[0][0])
+            if new_rec:
+                curs.execute(db_change("update other set data = ? where name = 'recaptcha'"), [new_rec.groups()[0]])
+            else:
+                curs.execute(db_change("update other set data = '' where name = 'recaptcha'"))
+                curs.execute(db_change("update other set data = '' where name = 'sec_re'"))
+    
+    if ver_num < 3172800 and set_data['db_type'] == 'mysql':
+        get_data_mysql = json.loads(open('data/mysql.json').read())
+        
+        with open('data/mysql.json', 'w') as f:
+            f.write('{ "user" : "' + get_data_mysql['user'] + '", "password" : "' + get_data_mysql['password'] + '", "host" : "localhost" }')
+
     conn.commit()
     print('Update pass')
-    print('----')
 
 def set_init():
     # 초기값 설정 함수    
@@ -215,19 +217,16 @@ def set_init():
         for i in ['naver.com', 'gmail.com', 'daum.net', 'kakao.com']:
             curs.execute(db_change("insert into html_filter (html, kind) values (?, 'email')"), [i])
 
+    curs.execute(db_change("select html from html_filter where kind = 'extension'"))
+    if not curs.fetchall():
+        for i in ['jpg', 'jpeg', 'png', 'gif', 'webp']:
+            curs.execute(db_change("insert into html_filter (html, kind) values (?, 'extension')"), [i])
+
     curs.execute(db_change('select data from other where name = "smtp_server" or name = "smtp_port" or name = "smtp_security"'))
     if not curs.fetchall():
         for i in [['smtp_server', 'imap.google.com'], ['smtp_port', '587'], ['smtp_security', 'tls']]:
             curs.execute(db_change("insert into other (name, data) values (?, ?)"), [i[0], i[1]])
 
-def topic_change(num):
-    curs.execute(db_change('select title, sub from topic where id = "1" and code = ?'), [str(num)])
-    db_data = curs.fetchall()
-    if db_data:
-        return [db_data[0][0], db_data[0][1]]
-    else:
-        return ['Test', 'Test']
-
 def pw_encode(data, data2 = '', type_d = ''):
     if type_d == '':
         curs.execute(db_change('select data from other where name = "encode"'))
@@ -287,6 +286,39 @@ def pw_check(data, data2, type_d = 'no', id_d = ''):
 
     return re_data
 
+def captcha_get():
+    data = ''
+
+    if ip_or_user() != 0:
+        curs.execute(db_change('select data from other where name = "recaptcha"'))
+        recaptcha = curs.fetchall()
+        if recaptcha and recaptcha[0][0] != '':
+            curs.execute(db_change('select data from other where name = "sec_re"'))
+            sec_re = curs.fetchall()
+            if sec_re and sec_re[0][0] != '':
+                curs.execute(db_change('select data from other where name = "recaptcha_ver"'))
+                rec_ver = curs.fetchall()
+                if not rec_ver or rec_ver == '':
+                    data += '' + \
+                        '<script src="https://www.google.com/recaptcha/api.js" async defer></script>' + \
+                        '<div class="g-recaptcha" data-sitekey="' + recaptcha[0][0] + '"></div>' + \
+                        '<hr class=\"main_hr\">' + \
+                    ''
+                else:
+                    data += '' + \
+                        '<script src="https://www.google.com/recaptcha/api.js?render=' + recaptcha[0][0] + '"></script>' + \
+                        '<input type="hidden" id="g-recaptcha" name="g-recaptcha">' + \
+                        '<script type="text/javascript">' + \
+                            'grecaptcha.ready(function() {' + \
+                                'grecaptcha.execute(\'' + recaptcha[0][0] + '\', {action: \'homepage\'}).then(function(token) {' + \
+                                    'document.getElementById(\'g-recaptcha\').value = token;' + \
+                                '});' + \
+                            '});' + \
+                        '</script>' + \
+                    ''
+
+    return data
+
 def captcha_post(re_data, num = 1):
     if num == 1:
         curs.execute(db_change('select data from other where name = "sec_re"'))
@@ -295,7 +327,7 @@ def captcha_post(re_data, num = 1):
             try:
                 data = urllib.request.urlopen('https://www.google.com/recaptcha/api/siteverify?secret=' + sec_re[0][0] + '&response=' + re_data)
             except:
-                pass
+                data = None
 
             if data and data.getcode() == 200:
                 json_data = json.loads(data.read().decode(data.headers.get_content_charset()))
@@ -467,7 +499,7 @@ def other2(data):
         data += ['']
 
     req_list = ''
-    main_css_ver = 19
+    main_css_ver = 22
 
     if not 'main_css_load' in flask.session or not 'main_css_ver' in flask.session or flask.session['main_css_ver'] != main_css_ver:
         for i_data in os.listdir(os.path.join("views", "main_css", "css")):
@@ -491,7 +523,7 @@ def other2(data):
                 integrity="sha384-2BKqo+exmr9su6dir+qCw08N2ZKRucY4PrGQPPWU1A7FtlCGjmEGFqXCv5nyM5Ij"
                 crossorigin="anonymous"></script>
         <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
-    ''' + req_list] + data[2:]
+    ''' + req_list + '<script>main_css_skin_load();</script>'] + data[2:]
 
     return data
 
@@ -804,43 +836,56 @@ def slow_edit_check():
 
     return 0
 
-def acl_check(name = 'test', tool = '', sub = 'test'):
+def acl_check(name = 'test', tool = '', topic_num = '1'):
     ip = ip_check()
-
-    if ban_check() == 1:
-        return 1
-
-    if tool == '' and tool != 'render' and name:
+    get_ban = ban_check()
+    
+    if name:
         acl_c = re.search("^user:((?:(?!\/).)*)", name)
-        if acl_c:
-            acl_n = acl_c.groups()
-
-            if admin_check(5) == 1:
-                return 0
+    else:
+        acl_c = None
 
-            curs.execute(db_change("select decu from acl where title = ?"), ['user:' + acl_n[0]])
-            acl_data = curs.fetchall()
-            if acl_data:
-                if acl_data[0][0] == 'all':
-                    return 0
+    if tool == '' and acl_c:
+        acl_n = acl_c.groups()
 
-                if acl_data[0][0] == 'user' and not re.search("(\.|:)", ip):
-                    return 0
+        if get_ban == 1:
+            return 1
 
-                if ip != acl_n[0] or re.search("(\.|:)", ip):
-                    return 1
+        if admin_check(5) == 1:
+            return 0
 
-            if ip == acl_n[0] and not re.search("(\.|:)", ip) and not re.search("(\.|:)", acl_n[0]):
+        curs.execute(db_change("select decu from acl where title = ?"), ['user:' + acl_n[0]])
+        acl_data = curs.fetchall()
+        if acl_data:
+            if acl_data[0][0] == 'all':
+                return 0
+            elif acl_data[0][0] == 'user' and not ip_or_user(ip) == 1:
+                return 0
+            elif ip == acl_n[0] and not ip_or_user(ip) == 1:
+                return 0
+        else:
+            if ip == acl_n[0] and not ip_or_user(ip) == 1 and not ip_or_user(acl_n[0]) == 1:
                 return 0
-            else:
-                return 1
 
-        if re.search("^file:", name) and admin_check(None, 'file edit (' + name + ')') != 1:
-            return 1
+        return 1
 
+    if tool == '' or tool == 'edit_req':
+        if acl_check(name, 'render') == 1:
+            return 1
+    
     if tool == '':
         end = 3
-    elif tool == 'topic' or tool == 'render':
+    elif tool == 'topic':
+        if not name:
+            curs.execute(db_change("select title from rd where code = ?"), [topic_num])
+            topic_data = curs.fetchall()
+            if topic_data:
+                name = topic_data[0][0]
+            else:
+                name = 'test'
+
+        end = 3
+    elif tool == 'render':
         end = 2
     else:
         end = 1
@@ -856,7 +901,9 @@ def acl_check(name = 'test', tool = '', sub = 'test'):
 
             num = 5
         elif tool == 'topic':
-            if i == 0:
+            if i == 0 and topic_num:
+                curs.execute(db_change("select acl from rd where code = ?"), [topic_num])
+            elif i == 1:
                 curs.execute(db_change("select dis from acl where title = ?"), [name])
             else:
                 curs.execute(db_change('select data from other where name = "discussion"'))
@@ -879,57 +926,60 @@ def acl_check(name = 'test', tool = '', sub = 'test'):
             num = 5
 
         acl_data = curs.fetchall()
-        if acl_data and acl_data[0][0] != 'normal':
-            if acl_data[0][0] == 'all':
+        if (not acl_data and i == (end - 1)) and get_ban == 1 and tool != 'render':
+            return 1
+        elif acl_data and acl_data[0][0] != 'normal' and acl_data[0][0] != '':
+            if acl_data[0][0] != 'ban' and get_ban == 1 and tool != 'render':
                 return 1
 
-            if acl_data[0][0] == 'user':
-                if ip_or_user(ip) == 1:
-                    return 1
-
-            if acl_data[0][0] == 'admin':
-                if ip_or_user(ip) == 1:
-                    return 1
-
-                if admin_check(num) != 1:
-                    return 1
-
-            if acl_data[0][0] == '50_edit':
-                if ip_or_user(ip) == 1:
-                    return 1
-
-                if admin_check(num) != 1:
-                    curs.execute(db_change("select count(title) from history where ip = ?"), [ip])
-                    count = curs.fetchall()
-                    if count:
-                        count = count[0][0]
+            if acl_data[0][0] == 'all' or acl_data[0][0] == 'ban':
+                return 0
+            elif acl_data[0][0] == 'user':
+                if ip_or_user(ip) != 1:
+                    return 0
+            elif acl_data[0][0] == 'admin':
+                if ip_or_user(ip) != 1:
+                    if admin_check(num) == 1:
+                        return 0
+            elif acl_data[0][0] == '50_edit':
+                if ip_or_user(ip) != 1:
+                    if admin_check(num) == 1:
+                        return 0
                     else:
-                        count = 0
-
-                    if count < 50:
-                        return 1
-
-            if acl_data[0][0] == 'email':
-                if ip_or_user(ip) == 1:
-                    return 1
-
-                if admin_check(num) != 1:
-                    curs.execute(db_change("select data from user_set where id = ? and name = 'email'"), [ip])
-                    email = curs.fetchall()
-                    if not email:
-                        return 1
-
-            if acl_data[0][0] == 'owner':
-                if admin_check() != 1:
-                    return 1
+                        curs.execute(db_change("select count(title) from history where ip = ?"), [ip])
+                        count = curs.fetchall()
+                        count = count[0][0] if count else 0
+                        if count >= 50:
+                            return 0
+            elif acl_data[0][0] == 'email':
+                if ip_or_user(ip) != 1:
+                    if admin_check(num) == 1:
+                        return 0
+                    else:
+                        curs.execute(db_change("select data from user_set where id = ? and name = 'email'"), [ip])
+                        if curs.fetchall():
+                            return 0
+            elif acl_data[0][0] == 'owner':
+                if admin_check() == 1:
+                    return 0
 
-        if tool == 'topic':
-            curs.execute(db_change("select title from rd where title = ? and sub = ? and not stop = ''"), [name, sub])
-            if curs.fetchall():
-                if admin_check(3, 'topic (' + name + ')') != 1:
-                    return 1
+            return 1
+        else:
+            if i == (end - 1):
+                if tool == 'topic':
+                    if topic_num:
+                        curs.execute(db_change("select title from rd where code = ? and stop != ''"), [topic_num])
+                        if curs.fetchall():
+                            if admin_check(3, 'topic (code ' + topic_num + ')') == 1:
+                                return 0
+                        else:
+                            return 0
+                    else:
+                        return 0
+                else:
+                    return 0
 
-    return 0
+    return 1
 
 def ban_check(ip = None, tool = None):
     if not ip:
@@ -1017,17 +1067,30 @@ def ban_insert(name, end, why, login, blocker, type_d = None):
         else:
             r_time = ''
 
-        curs.execute(db_change("insert into rb (block, end, today, blocker, why, band) values (?, ?, ?, ?, ?, ?)"), [name, r_time, now_time, blocker, why, band])
-        curs.execute(db_change("insert into ban (block, end, why, band, login) values (?, ?, ?, ?, ?)"), [name, r_time, why, band, login])
+        curs.execute(db_change("insert into rb (block, end, today, blocker, why, band) values (?, ?, ?, ?, ?, ?)"), [
+            name, 
+            r_time, 
+            now_time, 
+            blocker, 
+            why, 
+            band
+        ])
+        curs.execute(db_change("insert into ban (block, end, why, band, login) values (?, ?, ?, ?, ?)"), [
+            name, 
+            r_time, 
+            why, 
+            band, 
+            login
+        ])
 
     conn.commit()
 
-def rd_plus(title, sub, date):
-    curs.execute(db_change("select title from rd where title = ? and sub = ?"), [title, sub])
+def rd_plus(topic_num, date, name = None, sub = None):
+    curs.execute(db_change("select code from rd where code = ?"), [topic_num])
     if curs.fetchall():
-        curs.execute(db_change("update rd set date = ? where title = ? and sub = ?"), [date, title, sub])
+        curs.execute(db_change("update rd set date = ? where code = ?"), [date, topic_num])
     else:
-        curs.execute(db_change("insert into rd (title, sub, date) values (?, ?, ?)"), [title, sub, date])
+        curs.execute(db_change("insert into rd (title, sub, code, date) values (?, ?, ?, ?)"), [name, sub, topic_num, date])
 
     conn.commit()
 
@@ -1097,6 +1160,14 @@ def edit_filter_do(data):
 def redirect(data = '/'):
     return flask.redirect(data)
 
+def get_acl_list(type_d = None):
+    acl_data = ['', 'all', 'user', 'admin', 'owner', '50_edit', 'email', 'ban']
+    if type_d:
+        if type_d == 'user':
+            acl_data = ['', 'user', 'all']
+
+    return acl_data
+
 def re_error(data):
     conn.commit()
 
@@ -1112,81 +1183,86 @@ def re_error(data):
             menu = 0
         ))
     else:
-        error_data = re.search('\/error\/([0-9]+)', data)
-        if error_data:
-            num = int(error_data.groups()[0])
-            if num == 1:
-                data = load_lang('no_login_error')
-            elif num == 2:
-                data = load_lang('no_exist_user_error')
-            elif num == 3:
-                data = load_lang('authority_error')
-            elif num == 4:
-                data = load_lang('no_admin_block_error')
-            elif num == 5:
-                data = load_lang('skin_error')
-            elif num == 6:
-                data = load_lang('same_id_exist_error')
-            elif num == 7:
-                data = load_lang('long_id_error')
-            elif num == 8:
-                data = load_lang('id_char_error') + ' <a href="/name_filter">(' + load_lang('id') + ' ' + load_lang('filter') + ')</a>'
-            elif num == 9:
-                data = load_lang('file_exist_error')
-            elif num == 10:
-                data = load_lang('password_error')
-            elif num == 11:
-                data = load_lang('topic_long_error')
-            elif num == 12:
-                data = load_lang('email_error')
-            elif num == 13:
-                data = load_lang('recaptcha_error')
-            elif num == 14:
-                data = load_lang('file_extension_error')
-            elif num == 15:
-                data = load_lang('edit_record_error')
-            elif num == 16:
-                data = load_lang('same_file_error')
-            elif num == 17:
-                data = load_lang('file_capacity_error') + wiki_set(3)
-            elif num == 19:
-                data = load_lang('decument_exist_error')
-            elif num == 20:
-                data = load_lang('password_diffrent_error')
-            elif num == 21:
-                data = load_lang('edit_filter_error')
-            elif num == 22:
-                data = load_lang('file_name_error')
-            elif num == 23:
-                data = load_lang('regex_error')
-            elif num == 24:
-                curs.execute(db_change("select data from other where name = 'slow_edit'"))
-                slow_data = curs.fetchall()
-                data = load_lang('fast_edit_error') + slow_data[0][0]
-            elif num == 25:
-                data = load_lang('too_many_dec_error')
-            elif num == 26:
-                data = load_lang('application_not_found')
-            elif num == 27:
-                data = load_lang("invalid_password_error")
-            elif num == 28:
-                data = load_lang('watchlist_overflow_error')
-            elif num == 29:
-                data = load_lang('copyright_disagreed')
-            else:
-                data = '???'
-
-            if num == 5:
-                return easy_minify(flask.render_template(skin_check(),
-                    imp = [load_lang('skin_set'), wiki_set(1), custom(), other2([0, 0])],
-                    data = '<div id="main_skin_set"><h2>' + load_lang('error') + '</h2><ul><li>' + data + '</li></ul></div>',
-                    menu = 0
-                ))
-            else:
-                return easy_minify(flask.render_template(skin_check(),
-                    imp = [load_lang('error'), wiki_set(1), custom(), other2([0, 0])],
-                    data = '<h2>' + load_lang('error') + '</h2><ul><li>' + data + '</li></ul>',
-                    menu = 0
-                )), 401
+        num = int(number_check(data.replace('/error/', '')))
+        if num == 1:
+            data = load_lang('no_login_error')
+        elif num == 2:
+            data = load_lang('no_exist_user_error')
+        elif num == 3:
+            data = load_lang('authority_error')
+        elif num == 4:
+            data = load_lang('no_admin_block_error')
+        elif num == 5:
+            data = load_lang('skin_error')
+        elif num == 6:
+            data = load_lang('same_id_exist_error')
+        elif num == 7:
+            data = load_lang('long_id_error')
+        elif num == 8:
+            data = load_lang('id_char_error') + ' <a href="/name_filter">(' + load_lang('id_filter_list') + ')</a>'
+        elif num == 9:
+            data = load_lang('file_exist_error')
+        elif num == 10:
+            data = load_lang('password_error')
+        elif num == 11:
+            data = load_lang('topic_long_error')
+        elif num == 12:
+            data = load_lang('email_error')
+        elif num == 13:
+            data = load_lang('recaptcha_error')
+        elif num == 14:
+            data = load_lang('file_extension_error') + ' <a href="/extension_filter">(' + load_lang('extension_filter_list') + ')</a>'
+        elif num == 15:
+            data = load_lang('edit_record_error')
+        elif num == 16:
+            data = load_lang('same_file_error')
+        elif num == 17:
+            data = load_lang('file_capacity_error') + wiki_set(3)
+        elif num == 19:
+            data = load_lang('decument_exist_error')
+        elif num == 20:
+            data = load_lang('password_diffrent_error')
+        elif num == 21:
+            data = load_lang('edit_filter_error')
+        elif num == 22:
+            data = load_lang('file_name_error')
+        elif num == 23:
+            data = load_lang('regex_error')
+        elif num == 24:
+            curs.execute(db_change("select data from other where name = 'slow_edit'"))
+            slow_data = curs.fetchall()
+            data = load_lang('fast_edit_error') + slow_data[0][0]
+        elif num == 25:
+            data = load_lang('too_many_dec_error')
+        elif num == 26:
+            data = load_lang('application_not_found')
+        elif num == 27:
+            data = load_lang("invalid_password_error")
+        elif num == 28:
+            data = load_lang('watchlist_overflow_error')
+        elif num == 29:
+            data = load_lang('copyright_disagreed')
+        else:
+            data = '???'
+
+        if num == 5:
+            get_url = flask.request.path
+        
+            return easy_minify(flask.render_template(skin_check(),
+                imp = [(load_lang('skin_set') if get_url != '/main_skin_set' else load_lang('main_skin_set')), wiki_set(1), custom(), other2([0, 0])],
+                data = '' + \
+                    '<div id="main_skin_set">' + \
+                        '<h2>' + load_lang('error') + '</h2>' + \
+                        '<ul>' + \
+                            '<li>' + data + '</li>' + \
+                        '</ul>' + \
+                    '</div>' + \
+                    ('<script>window.onload = function () { main_css_skin_set(); }</script>' if get_url == '/main_skin_set' else ''),
+                menu = ([['main_skin_set', load_lang('main_skin_set')]] if get_url != '/main_skin_set' else [['skin_set', load_lang('skin_set')]])
+            ))
         else:
-            return redirect('/')
+            return easy_minify(flask.render_template(skin_check(),
+                imp = [load_lang('error'), wiki_set(1), custom(), other2([0, 0])],
+                data = '<h2>' + load_lang('error') + '</h2><ul><li>' + data + '</li></ul>',
+                menu = 0
+            )), 401

+ 6 - 11
route/tool/mark.py

@@ -21,18 +21,13 @@ def send_parser(data):
     if not re.search('^<br>$', data):
         data = html.escape(data)
 
-        javascript = re.compile('javascript:', re.I)
+        data = re.sub('javascript:', '', data, flags = re.I)
+        data = data.replace('&lt;br&gt;', '')
 
-        data = javascript.sub('', data)
-
-        while 1:
-            re_data = re.search('&lt;a(?: (?:(?:(?!&gt;).)*))?&gt;(?P<in>(?:(?!&lt;).)*)&lt;\/a&gt;', data)
-            if re_data:
-                re_data = re_data.groups()[0]
-
-                data = re.sub('&lt;a(?: (?:(?:(?!&gt;).)*))?&gt;(?P<in>(?:(?!&lt;).)*)&lt;\/a&gt;', '<a href="/w/' + urllib.parse.quote(re_data).replace('/','%2F') + '">' + re_data + '</a>', data, 1)
-            else:
-                break
+    link_re = re.compile('&lt;a(?: (?:(?:(?!&gt;).)*))?&gt;(?P<in>(?:(?!&lt;).)*)&lt;\/a&gt;')
+    link_data = link_re.findall(data)
+    for i in link_data:
+        data = link_re.sub('<a href="/w/' + urllib.parse.quote(i).replace('/','%2F') + '">' + i + '</a>', data, 1)
 
     return data
 

+ 272 - 240
route/tool/set_mark/namumark.py

@@ -4,7 +4,14 @@ import datetime
 import html
 import re
 
+def nowiki_js(data):
+    return re.sub('^\n', '', data.replace('\\', '\\\\').replace('"', '\\"').replace('\r', '')).replace('\n', '<br>')
+
 def link_fix(main_link):
+    global end_data
+
+    main_link = main_link.replace('&#x27;', "<link_comma>")
+    
     if re.search('^:', main_link):
         main_link = re.sub('^:', '', main_link)
 
@@ -20,11 +27,20 @@ def link_fix(main_link):
     else:
         other_link = ''
 
+    main_link = main_link.replace("<link_comma>", "&#x27;")
     main_link = re.sub('\\\\#', '%23', main_link)
 
+    find_data = re.findall('<span id="(nowiki_[0-9]+)">', main_link)
+    for i in find_data:
+        main_link = main_link.replace('<span id="' + i + '"></span>', end_data[i])
+
+    find_data = re.findall('<span id="(nowiki_[0-9]+)">', other_link)
+    for i in find_data:
+        other_link = other_link.replace('<span id="' + i + '"></span>', end_data[i])
+
     return [main_link, other_link]
 
-def table_parser(data, cel_data, start_data, num = 0):
+def table_parser(data, cel_data, cel_num, start_data, num = 0, cel_color = {}):
     table_class = 'class="'
     div_style = 'style="'
     all_table = 'style="'
@@ -33,6 +49,11 @@ def table_parser(data, cel_data, start_data, num = 0):
     row = ''
     cel = 'colspan="' + str(round(len(start_data) / 2)) + '"'
 
+    if not cel_num in cel_color:
+        cel_color[cel_num] = ''
+
+    cel_style += cel_color[cel_num]
+
     if num == 0:
         if re.search('^ ', cel_data) and re.search(' $', cel_data):
             cel_style += 'text-align: center;'
@@ -95,8 +116,19 @@ def table_parser(data, cel_data, start_data, num = 0):
         elif re.search("^table ?color=([^=]+)$", in_state):
             table_data = re.sub('^table ?color=', '', in_state)
             all_table += 'color: ' + (re.sub(',([^,]*)', '', table_data) if re.search(',', table_data) else table_data) + ';'
+        elif re.search("^col ?bgcolor=([^=]+)$", in_state):
+            table_data = re.sub('^col ?bgcolor=', '', in_state)
+            table_data = (re.sub(',([^,]*)', '', table_data) if re.search(',', table_data) else table_data)
+            cel_color[cel_num] += 'background: ' + table_data + ';'
+            cel_style += 'background: ' + table_data + ';'
+        elif re.search("^col ?color=([^=]+)$", in_state):
+            table_data = re.sub('^col ?color=', '', in_state)
+            table_data = (re.sub(',([^,]*)', '', table_data) if re.search(',', table_data) else table_data)
+            cel_color[cel_num] += 'color: ' + table_data + ';'
+            cel_style += 'color: ' + table_data + ';'
         elif re.search("^(bgcolor=([^=]+)|#(?:[0-9a-f-A-F]{3}){1,2}|\w+)$", in_state):
-            cel_style += 'background: ' + re.sub('^bgcolor=', '', in_state) + ';'
+            table_data = re.sub('^bgcolor=', '', in_state)
+            cel_style += 'background: ' + (re.sub(',([^,]*)', '', table_data) if re.search(',', table_data) else table_data) + ';'
         elif re.search("^color=([^=]+)$", in_state):
             table_data = re.sub('^color=', '', in_state)
             cel_style += 'color: ' + (re.sub(',([^,]*)', '', table_data) if re.search(',', table_data) else table_data) + ';'
@@ -122,72 +154,97 @@ def table_parser(data, cel_data, start_data, num = 0):
     row_style += '"'
     table_class += '"'
 
-    return [all_table, row_style, cel_style, row, cel, table_class, num, div_style]
+    return [all_table, row_style, cel_style, row, cel, table_class, num, div_style, cel_color]
 
 def table_start(data):
     while 1:
+        cel_num = 0
+        table_num = 0
+        table_end = ''
+        cel_color = {}
+
         table = re.search('\n((?:(?:(?:(?:\|\||\|[^|]+\|)+(?:(?:(?!\|\|).\n*)*))+)\|\|(?:\n)?)+)', data)
         if table:
-            table = table.groups()[0]
-
-            all_table = re.search('^((?:\|\||\|[^|]+\|)+)((?:&lt;(?:(?:(?!&gt;).)+)&gt;)*)\n*((?:(?!\|\|).\n*)*)', table)
-            if all_table:
-                all_table = all_table.groups()
-
-                return_table = table_parser(all_table[1], all_table[2], re.sub('^\|([^|]+)\|', '||', all_table[0]))
-                number = return_table[6]
-
-                table_caption = re.search('^\|([^|]+)\|', table)
-                table_caption = '<caption>' + table_caption.groups()[0] + '</caption>' if table_caption else ''
+            table = re.sub('(\|\|)+\n', '||\n', table.groups()[0])
+            
+            table_caption = re.search('^\|([^|]+)\|', table)
+            if table_caption:
+                table_caption = '<caption>' + table_caption.groups()[0] + '</caption>'
+                
+                table = re.sub('^\|([^|]+)\|', '||', table)
+            else:
+                table_caption = ''
+            
+            table = '\n' + table
+            
+            table_cel = re.findall('(\n(?:(?:\|\|)+)|\|\|\n(?:(?:\|\|)+)|(?:(?:\|\|)+))((?:(?:(?!\n|\|\|).)+\n*)+)', table)
+            for i in table_cel:
+                cel_plus = re.search('^((?:&lt;(?:(?:(?!&gt;).)*)&gt;)+)', i[1])
+                cel_plus = cel_plus.groups()[0] if cel_plus else ''
+                cel_data = re.sub('^((?:&lt;(?:(?:(?!&gt;).)*)&gt;)+)', '', i[1])
+
+                if re.search('^\n', i[0]):
+                    cel_num = 1
+
+                    cel_plus = table_parser(
+                        cel_plus, 
+                        cel_data,
+                        cel_num,
+                        re.sub('^\n', '', i[0]),
+                        table_num,
+                        cel_color
+                    )
+                    cel_color = cel_plus[8]
+                    table_num = cel_plus[6]
 
-                table = re.sub(
-                    '^((?:\|\||\|[^|]+\|)+)((?:&lt;(?:(?:(?!&gt;).)+)&gt;)*)\n*',
-                    '\n' + \
-                        '<div class="table_safe" ' + return_table[7] + '>' + \
-                            '<table ' + return_table[5] + ' ' + return_table[0] + '>' + \
+                    table_end += '' + \
+                        '<div class="table_safe" ' + cel_plus[7] + '>' + \
+                            '<table ' + cel_plus[5] + ' ' + cel_plus[0] + '>' + \
                                 table_caption + \
-                                '<tbody>' + \
-                                    '<tr ' + return_table[1] + '>' + \
-                                        '<td ' + return_table[2] + ' ' + return_table[3] + ' ' + return_table[4] + '>',
-                    table,
-                    1
-                )
-
-            table = re.sub('\|\|\n?$', '</td></tr></tbody></table></div>', table)
-
-            while 1:
-                row_table = re.search('\|\|\n((?:\|\|)+)((?:&lt;(?:(?:(?!&gt;).)+)&gt;)*)\n*((?:(?!\|\||<\/td>).\n*)*)', table)
-                if row_table:
-                    row_table = row_table.groups()
-
-                    return_table = table_parser(row_table[1], row_table[2], row_table[0], number)
-
-                    table = re.sub(
-                        '\|\|\n((?:\|\|)+)((?:&lt;(?:(?:(?!&gt;).)+)&gt;)*)\n*',
-                        '</td></tr><tr ' + return_table[1] + '><td ' + return_table[2] + ' ' + return_table[3] + ' ' + return_table[4] + '>',
-                        table,
-                        1
+                                '<tr ' + cel_plus[1] + '>' + \
+                                    '<td ' + cel_plus[2] + ' ' + cel_plus[3] + ' ' + cel_plus[4] + '>' + \
+                                        cel_data
+                                        
+                elif re.search('\n', i[0]):
+                    cel_num = 1
+
+                    cel_plus = table_parser(
+                        cel_plus, 
+                        cel_data,
+                        cel_num,
+                        re.sub('^\|\|\n', '', i[0]),
+                        table_num,
+                        cel_color
                     )
+                    cel_color = cel_plus[8]
+
+                    table_end += '' + \
+                            '</td>' + \
+                        '</tr>' + \
+                        '<tr ' + cel_plus[1] + '>' + \
+                            '<td ' + cel_plus[2] + ' ' + cel_plus[3] + ' ' + cel_plus[4] + '>' + \
+                                cel_data
                 else:
-                    break
-
-            while 1:
-                cel_table = re.search('((?:\|\|)+)((?:&lt;(?:(?:(?!&gt;).)+)&gt;)*)\n*((?:(?:(?!\|\||<\/td>).)|\n)*\n*)', table)
-                if cel_table:
-                    cel_table = cel_table.groups()
+                    cel_num += 1
+
+                    cel_plus = table_parser(
+                        cel_plus, 
+                        cel_data,
+                        cel_num,
+                        re.sub('^\|\|\n', '', i[0]),
+                        table_num,
+                        cel_color
+                    )
+                    cel_color = cel_plus[8]
 
-                    return_table = table_parser(cel_table[1], re.sub('\n', ' ', cel_table[2]), cel_table[0], number)
+                    table_end += '' + \
+                        '</td>' + \
+                        '<td ' + cel_plus[2] + ' ' + cel_plus[3] + ' ' + cel_plus[4] + '>' + \
+                            cel_data
 
-                    table = re.sub(
-                        '((?:\|\|)+)((?:&lt;(?:(?:(?!&gt;).)+)&gt;)*)\n*',
-                        '</td><td ' + return_table[2] + ' ' + return_table[3] + ' ' + return_table[4] + '>',
-                        table,
-                        1
-                    )
-                else:
-                    break
+            table_end += '</td></tr></table></div>'
 
-            data = re.sub('\n((?:(?:(?:(?:\|\||\|[^|]+\|)+(?:(?:(?!\|\|).\n*)*))+)\|\|(?:\n)?)+)', table, data, 1)
+            data = re.sub('\n((?:(?:(?:(?:\|\||\|[^|]+\|)+(?:(?:(?!\|\|).\n*)*))+)\|\|(?:\n)?)+)', '\n' + table_end + '\n', data, 1)
         else:
             break
 
@@ -196,6 +253,7 @@ def table_start(data):
 def middle_parser(data, include_num):
     global end_data
     global plus_data
+    global nowiki_num
 
     middle_stack = 0
     middle_list = []
@@ -212,7 +270,7 @@ def middle_parser(data, include_num):
             if middle_stack > 0:
                 middle_stack += 1
 
-                data = re.sub('(?:{{{((?:(?! |{{{|}}}|&lt;).)*)(?P<in> ?)|(}}}))', '&#123;&#123;&#123;' + middle_data[0] + '\g<in>', data, 1)
+                data = re.sub('(?:{{{((?:(?! |{{{|}}}|&lt;).)*)(?P<in> ?)|(}}}))', '<middle_start>' + middle_data[0] + '\g<in>', data, 1)
             else:
                 if re.search('^(#|@|\+|\-)', middle_data[0]) and not re.search('^(#|@|\+|\-){2}|(#|@|\+|\-)\\\\', middle_data[0]):
                     if re.search('^(#(?:[0-9a-f-A-F]{3}){1,2})', middle_data[0]):
@@ -326,13 +384,13 @@ def middle_parser(data, include_num):
                 middle_num += 1
         else:
             if middle_list == []:
-                data = middle_re.sub('&#125;&#125;&#125;', data, 1)
+                data = middle_re.sub('<middle_end>', data, 1)
             else:
                 if middle_stack > 0:
                     middle_stack -= 1
 
                 if middle_stack > 0:
-                    data = middle_re.sub('&#125;&#125;&#125;', data, 1)
+                    data = middle_re.sub('<middle_end>', data, 1)
                 else:
                     if middle_num > 0:
                         middle_num -= 1
@@ -351,13 +409,13 @@ def middle_parser(data, include_num):
             break
         else:
             if middle_list == []:
-                data += '&#125;&#125;&#125;'
+                data += '<middle_end>'
             else:
                 if middle_stack > 0:
                     middle_stack -= 1
 
                 if middle_stack > 0:
-                    data += '&#125;&#125;&#125;'
+                    data += '<middle_end>'
                 else:
                     if middle_num > 0:
                         middle_num -= 1
@@ -371,37 +429,39 @@ def middle_parser(data, include_num):
 
                     del(middle_list[middle_num])
 
-    num = 0
+    data = data.replace('<middle_start>', '{{{')
+    data = data.replace('<middle_end>', '}}}')
+
     while 1:
         nowiki_data = re.search('<code>((?:(?:(?!<\/code>).)*\n*)*)<\/code>', data)
         if nowiki_data:
             nowiki_data = nowiki_data.groups()
 
-            num += 1
-            end_data['nowiki_' + str(num)] = [nowiki_data[0], 'code']
+            nowiki_num += 1
+            end_data['nowiki_' + str(nowiki_num)] = nowiki_data[0]
+            plus_data += 'document.getElementById("nowiki_' + str(nowiki_num) + '").innerHTML = "' + nowiki_js(nowiki_data[0]) + '";\n'
 
             data = re.sub(
                 '<code>((?:(?:(?!<\/code>).)*\n*)*)<\/code>',
-                '<span id="nowiki_' + str(num) + '"></span>',
+                '<span id="nowiki_' + str(nowiki_num) + '"></span>',
                 data,
                 1
             )
         else:
             break
 
-    num = 0
     while 1:
-        syntax_data = re.search('<code class="((?:(?!"|>|<).)+)">((?:\n*(?:(?:(?!<\/code>|<span id="syntax_).)+)\n*)+)<\/code>', data)
+        syntax_data = re.search('<code class="((?:(?!"|>|<).)+)">((?:\n*(?:(?:(?!<\/code>|<span id="nowiki_).)+)\n*)+)<\/code>', data)
         if syntax_data:
             syntax_data = syntax_data.groups()
 
-            num += 1
-
-            end_data['syntax_' + str(num)] = [syntax_data[1], 'normal']
+            nowiki_num += 1
+            end_data['nowiki_' + str(nowiki_num)] = syntax_data[1]
+            plus_data += 'document.getElementById("nowiki_' + str(nowiki_num) + '").innerHTML = "' + nowiki_js(syntax_data[1]) + '";\n'
 
             data = re.sub(
                 '<code class="((?:(?!"|>|<).)+)">((?:\n*(?:(?:(?!<\/code>|<span id="syntax_).)+)\n*)+)<\/code>',
-                '<code class="' + syntax_data[0] + '"><span id="syntax_' + str(num) + '"></span></code>',
+                '<code class="' + syntax_data[0] + '"><span id="nowiki_' + str(nowiki_num) + '"></span></code>',
                 data,
                 1
             )
@@ -415,7 +475,9 @@ def namumark(conn, data, title, main_num, include_num):
 
     global plus_data
     global end_data
+    global nowiki_num
 
+    nowiki_num = 0
     data = '\n' + data + '\n'
     include_num = include_num + '_' if include_num else ''
     plus_data = 'get_link_state("' + include_num + '");\nget_file_state("' + include_num + '");\n'
@@ -428,45 +490,60 @@ def namumark(conn, data, title, main_num, include_num):
     data = html.escape(data)
     data = re.sub('\r\n', '\n', data)
 
-    first = 0
     math_re = re.compile('\[math\(((?:(?!\)\]).)+)\)\]', re.I)
     while 1:
         math = math_re.search(data)
         if math:
             math = math.groups()[0]
+            math = math.replace('{', '<math_mid_1>')
+            math = math.replace('}', '<math_mid_2>')
+            math = math.replace('\\', '<math_slash>')
 
-            first += 1
+            data = math_re.sub('<math>' + math + '</math>', data, 1)
+        else:
+            break
+
+    data = data.replace('\\{', '<break_middle>')
+    data = middle_parser(data, include_num)
+    data = data.replace('<break_middle>', '\\{')
 
+    first = 0
+    math_re = re.compile('<math>((?:(?!<\/math>).)+)<\/math>', re.I)
+    while 1:
+        math = math_re.search(data)
+        if math:
+            math = math.groups()[0]
+            math = math.replace('<math_mid_1>', '{')
+            math = math.replace('<math_mid_2>', '}')
+            math = math.replace('<math_slash>', '\\')
+
+            first += 1
             data = math_re.sub('<span id="math_' + str(first) + '"></span>', data, 1)
 
-            plus_data += '''
-                try {
-                    katex.render(
-                        "''' + html.unescape(math).replace('\\', '\\\\').replace('"', '\\"') + '''",
-                        document.getElementById("math_''' + str(first) + '''")
-                    );
-                } catch {
-                    document.getElementById("math_''' + str(first) + '''").innerHTML = '<span style="color: red;">''' + math.replace('\\', '\\\\') + '''</span>';
-                }\n
-            '''
+            plus_data += '' + \
+                'try {' + \
+                    'katex.render(' + \
+                        '"' + nowiki_js(html.unescape(math)) + '",' + \
+                        'document.getElementById(\"math_' + str(first) + '\")' + \
+                    ');' + \
+                '} catch {' + \
+                    'document.getElementById(\"math_' + str(first) + '\").innerHTML = "<span style=\'color: red;\'>' + nowiki_js(math) + '</span>";' + \
+                '}\n' + \
+            ''
         else:
             break
 
-    data = re.sub('\\\\{', '<break_middle>', data)
-    data = middle_parser(data, include_num)
-    data = re.sub('<break_middle>', '\\{', data)
-
     num = 0
     while 1:
         one_nowiki = re.search('(?:\\\\)(.)', data)
         if one_nowiki:
             one_nowiki = one_nowiki.groups()
 
-            num += 1
-
-            end_data['one_nowiki_' + str(num)] = [one_nowiki[0], 'normal']
+            nowiki_num += 1
+            end_data['nowiki_' + str(nowiki_num)] = one_nowiki[0]
+            plus_data += 'document.getElementById("nowiki_' + str(nowiki_num) + '").innerHTML = "' + nowiki_js(one_nowiki[0]) + '";\n'
 
-            data = re.sub('(?:\\\\)(.)', '<span id="one_nowiki_' + str(num) + '"></span>', data, 1)
+            data = re.sub('(?:\\\\)(.)', '<span id="nowiki_' + str(nowiki_num) + '"></span>', data, 1)
         else:
             break
 
@@ -500,9 +577,9 @@ def namumark(conn, data, title, main_num, include_num):
                     include_plus = include_plus.groups()
 
                     include_data_set = include_plus[1]
-                    find_data = re.findall('<span id="(one_nowiki_[0-9]+)">', include_data_set)
+                    find_data = re.findall('<span id="(nowiki_[0-9]+)">', include_data_set)
                     for j in find_data:
-                        include_data_set = include_data_set.replace('<span id="' + j + '"></span>', end_data[j][0])
+                        include_data_set = include_data_set.replace('<span id="' + j + '"></span>', end_data[j])
 
                     include_plus_data += [[include_plus[0], include_data_set]]
 
@@ -532,7 +609,12 @@ def namumark(conn, data, title, main_num, include_num):
             else:
                 end_parser = wiki_table_data[1]
 
-            data = re.sub('<div id="wiki_div" ((?:(?!>).)+)>((?:(?!<div id="wiki_div"|<\/div_1>).\n*)+)<\/div_1>', '<div ' + wiki_table_data[0] + '>' + end_parser + '</div_2>', data, 1)
+            data = re.sub(
+                '<div id="wiki_div" ((?:(?!>).)+)>((?:(?!<div id="wiki_div"|<\/div_1>).\n*)+)<\/div_1>',
+                '<div ' + wiki_table_data[0] + '>' + end_parser + '</div_2>', 
+                data, 
+                1
+            )
         else:
             break
 
@@ -548,7 +630,7 @@ def namumark(conn, data, title, main_num, include_num):
         redirect = redirect.groups()[0]
 
         return_link = link_fix(redirect)
-        main_link = return_link[0]
+        main_link = html.unescape(return_link[0])
         other_link = return_link[1]
 
         backlink += [[title, main_link + other_link, 'redirect']]
@@ -656,102 +738,19 @@ def namumark(conn, data, title, main_num, include_num):
 
     toc_data += '</div>'
     data = toc_re.sub(toc_data, data)
-
+    
     data = tool.savemark(data)
-
-    data = re.sub("\[anchor\((?P<in>(?:(?!\)\]).)+)\)\]", '<span id="\g<in>"></span>', data, flags = re.I)
-
-    ruby_all = re.findall("\[ruby\(((?:(?:(?!\)\]).)+))\)\]", data, flags = re.I)
-    for i in ruby_all:
-        ruby_code = re.search('^([^,]+)', i)
-        if ruby_code:
-            ruby_code = ruby_code.groups()[0]
-        else:
-            ruby_code = 'Test'
-
-        ruby_top = re.search('ruby=([^,]+)', i, flags = re.I)
-        if ruby_top:
-            ruby_top = ruby_top.groups()[0]
-        else:
-            ruby_top = 'Test'
-
-        ruby_color = re.search('color=([^,]+)', i, flags = re.I)
-        if ruby_color:
-            ruby_color = 'color: ' + ruby_color.groups()[0] + ';'
-        else:
-            ruby_color = ''
-
-        ruby_data = '' + \
-            '<ruby>' + \
-                ruby_code \
-                + '<rp>(</rp>' + \
-                '<rt style="' + ruby_color + '">' + ruby_top + '</rt>' + \
-                '<rp>)</rp>' + \
-            '</ruby>' + \
-        ''
-
-        data = re.sub("\[ruby\(((?:(?:(?!\)\]).)+))\)\]", ruby_data, data, 1, flags = re.I)
-
-    curs.execute(tool.db_change('select data from other where name = "count_all_title"'))
-    all_title = curs.fetchall()
-    data = re.sub('\[pagecount\]', all_title[0][0], data, flags = re.I)
-
+    
     now_time = tool.get_time()
-    data = re.sub('\[date\]', now_time, data, flags = re.I)
-
     time_data = re.search('^([0-9]{4}-[0-9]{2}-[0-9]{2})', now_time)
-    time = time_data.groups()
-
-    age_re = re.compile('\[age\(([0-9]{4}-[0-9]{2}-[0-9]{2})\)\]', re.I)
-    while 1:
-        age_data = age_re.search(data)
-        if age_data:
-            age = age_data.groups()[0]
-
-            try:
-                old = datetime.datetime.strptime(time[0], '%Y-%m-%d')
-                will = datetime.datetime.strptime(age, '%Y-%m-%d')
-
-                e_data = old - will
-
-                data = age_re.sub(str(int(e_data.days / 365)), data, 1)
-            except:
-                data = age_re.sub('age-error', data, 1)
-        else:
-            break
-
-    dday_re = re.compile('\[dday\(([0-9]{4}-[0-9]{2}-[0-9]{2})\)\]', re.I)
-    while 1:
-        dday_data = dday_re.search(data)
-        if dday_data:
-            dday = dday_data.groups()[0]
-
-            try:
-                old = datetime.datetime.strptime(time[0], '%Y-%m-%d')
-                will = datetime.datetime.strptime(dday, '%Y-%m-%d')
-
-                e_data = old - will
-
-                if re.search('^-', str(e_data.days)):
-                    e_day = str(e_data.days)
-                else:
-                    e_day = '+' + str(e_data.days)
-
-                data = dday_re.sub(e_day, data, 1)
-            except:
-                data = dday_re.sub('dday-error', data, 1)
-        else:
-            break
-
-    video_re = re.compile('\[(youtube|kakaotv|nicovideo)\(((?:(?!\)\]).)+)\)\]', re.I)
-    youtube_re = re.compile('youtube', re.I)
-    kakaotv_re = re.compile('kakaotv', re.I)
-    while 1:
-        video = video_re.search(data)
-        if video:
-            video = video.groups()
-
-            width = re.search(', ?width=((?:(?!,).)+)', video[1])
+    time = time_data.groups()[0]
+    
+    macro_re = re.compile('\[([^[(]+)\(((?:(?!\)]).)+)\)\]')
+    macro_data = macro_re.findall(data)
+    for i in macro_data:
+        macro_name = i[0].lower()
+        if macro_name == 'youtube' or macro_name == 'kakaotv' or macro_name == 'nicovideo':
+            width = re.search(', ?width=((?:(?!,).)+)', i[1])
             if width:
                 video_width = width.groups()[0]
                 if re.search('^[0-9]+$', video_width):
@@ -759,7 +758,7 @@ def namumark(conn, data, title, main_num, include_num):
             else:
                 video_width = '560px'
 
-            height = re.search(', ?height=((?:(?!,).)+)', video[1])
+            height = re.search(', ?height=((?:(?!,).)+)', i[1])
             if height:
                 video_height = height.groups()[0]
                 if re.search('^[0-9]+$', video_height):
@@ -767,7 +766,7 @@ def namumark(conn, data, title, main_num, include_num):
             else:
                 video_height = '315px'
 
-            code = re.search('^((?:(?!,).)+)', video[1])
+            code = re.search('^((?:(?!,).)+)', i[1])
             if code:
                 video_code = code.groups()[0]
             else:
@@ -775,8 +774,8 @@ def namumark(conn, data, title, main_num, include_num):
 
             video_start = ''
 
-            if youtube_re.search(video[0]):
-                start = re.search(', ?(start=(?:(?!,).)+)', video[1])
+            if macro_name == 'youtube':
+                start = re.search(', ?(start=(?:(?!,).)+)', i[1])
                 if start:
                     video_start = '?' + start.groups()[0]
 
@@ -784,7 +783,7 @@ def namumark(conn, data, title, main_num, include_num):
                 video_code = re.sub('^https:\/\/youtu\.be\/', '', video_code)
 
                 video_src = 'https://www.youtube.com/embed/' + video_code
-            elif kakaotv_re.search(video[0]):
+            elif macro_name == 'kakaotv':
                 video_code = re.sub('^https:\/\/tv\.kakao\.com\/channel\/9262\/cliplink\/', '', video_code)
                 video_code = re.sub('^http:\/\/tv\.kakao\.com\/v\/', '', video_code)
 
@@ -792,13 +791,72 @@ def namumark(conn, data, title, main_num, include_num):
             else:
                 video_src = 'https://embed.nicovideo.jp/watch/' + video_code
 
-            data = video_re.sub(
-                '<iframe style="width: ' + video_width + '; height: ' + video_height + ';" src="' + video_src + video_start + '" allowfullscreen></iframe>', 
+            data = macro_re.sub(
+                '<iframe style="width: ' + video_width + '; height: ' + video_height + ';" src="' + video_src + video_start + '" frameborder="0" allowfullscreen></iframe>', 
                 data, 
                 1
             )
+        elif macro_name == 'anchor':
+            data = macro_re.sub('<span id="' + i[1] + '"></span>', data, 1)
+        elif macro_name == 'ruby':
+            ruby_code = re.search('^([^,]+)', i[1])
+            if ruby_code:
+                ruby_code = ruby_code.groups()[0]
+            else:
+                ruby_code = 'Test'
+
+            ruby_top = re.search('ruby=([^,]+)', i[1], flags = re.I)
+            if ruby_top:
+                ruby_top = ruby_top.groups()[0]
+            else:
+                ruby_top = 'Test'
+
+            ruby_color = re.search('color=([^,]+)', i[1], flags = re.I)
+            if ruby_color:
+                ruby_color = 'color: ' + ruby_color.groups()[0] + ';'
+            else:
+                ruby_color = ''
+
+            ruby_data = '' + \
+                '<ruby>' + \
+                    ruby_code \
+                    + '<rp>(</rp>' + \
+                    '<rt style="' + ruby_color + '">' + ruby_top + '</rt>' + \
+                    '<rp>)</rp>' + \
+                '</ruby>' + \
+            ''
+
+            data = macro_re.sub(ruby_data, data, 1)
+        elif macro_name == 'age' or macro_name == 'dday':
+            try:
+                old = datetime.datetime.strptime(time, '%Y-%m-%d')
+                will = datetime.datetime.strptime(i[1], '%Y-%m-%d')
+
+                e_data = old - will
+
+                if macro_name == 'age':
+                    data = macro_re.sub(str(int(e_data.days / 365)), data, 1)
+                else:
+                    if re.search('^-', str(e_data.days)):
+                        e_day = str(e_data.days)
+                    else:
+                        e_day = '+' + str(e_data.days)
+
+                    data = macro_re.sub(e_day, data, 1)
+            except:
+                data = macro_re.sub('age-dday-error', data, 1)
         else:
-            break
+            data = macro_re.sub('<macro_start>' + i[0] + '<macro_middle>' + i[1] + '<macro_end>', data, 1)
+            
+    data = data.replace('<macro_start>', '[')
+    data = data.replace('<macro_middle>', '(')
+    data = data.replace('<macro_end>', ')]')
+
+    if re.search('\[pagecount\]', data, flags = re.I):
+        plus_data += 'page_count();\n'
+        data = re.sub('\[pagecount\]', '<span class="all_page_count"></span>', data, flags = re.I)
+
+    data = re.sub('\[date\]', now_time, data, flags = re.I)
 
     while 1:
         block = re.search('(\n(?:&gt; ?(?:(?:(?!\n).)+)?\n)+)', data)
@@ -846,6 +904,7 @@ def namumark(conn, data, title, main_num, include_num):
             break
 
     data = re.sub('<\/ul>\n \|\|', '</ul>||', data)
+    data = re.sub('\|\|</blockquote>', '</blockquote>||', data)
 
     while 1:
         indent = re.search('\n( +)', data)
@@ -863,7 +922,7 @@ def namumark(conn, data, title, main_num, include_num):
     category = ''
     category_re = re.compile('^(?:category|분류):', re.I)
     while 1:
-        link = re.search('\[\[((?:(?!\[\[|\]\]).)+)\]\]', data)
+        link = re.search('\[\[((?:(?!\[\[|\]\]|<\/td>).)+)\]\]', data)
         if link:
             link = link.groups()[0]
 
@@ -936,7 +995,7 @@ def namumark(conn, data, title, main_num, include_num):
 
                 if exist:
                     data = re.sub(
-                        '\[\[((?:(?!\[\[|\]\]).)+)\]\]',
+                        '\[\[((?:(?!\[\[|\]\]|<\/td>).)+)\]\]',
                         '<span style="' + file_align + '">' + \
                             '<span style="' + file_color + '">' + \
                                 '<img style="' + file_style + '" alt="' + file_alt + '" src="' + file_src + '">' + \
@@ -947,7 +1006,7 @@ def namumark(conn, data, title, main_num, include_num):
                     )
                 else:
                     data = re.sub(
-                        '\[\[((?:(?!\[\[|\]\]).)+)\]\]',
+                        '\[\[((?:(?!\[\[|\]\]|<\/td>).)+)\]\]',
                         '<span style="' + file_align + '">' + \
                             '<span style="' + file_color + '">' + \
                                 '<img class="' + include_num + 'file_finder_1" style="' + file_style + '" alt="' + file_alt + '" src="' + file_src + '">' + \
@@ -973,10 +1032,10 @@ def namumark(conn, data, title, main_num, include_num):
                 backlink += [[title, main_link, 'cat']]
                 category += '<a class="' + include_num + 'link_finder" href="/w/' + tool.url_pas(main_link) + '">' + category_re.sub('', see_link) + '</a> | '
 
-                data = re.sub('\[\[((?:(?!\[\[|\]\]).)+)\]\]', '', data, 1)
+                data = re.sub('\[\[((?:(?!\[\[|\]\]|<\/td>).)+)\]\]', '', data, 1)
             elif re.search('^wiki:', main_link):
                 data = re.sub(
-                    '\[\[((?:(?!\[\[|\]\]).)+)\]\]',
+                    '\[\[((?:(?!\[\[|\]\]|<\/td>).)+)\]\]',
                     '<a id="inside" href="/' + tool.url_pas(re.sub('^wiki:', '', main_link)) + '">' + see_link + '</a>',
                     data,
                     1
@@ -995,30 +1054,30 @@ def namumark(conn, data, title, main_num, include_num):
 
                     if see_link != main_link:
                         data = re.sub(
-                            '\[\[((?:(?!\[\[|\]\]).)+)\]\]',
+                            '\[\[((?:(?!\[\[|\]\]|<\/td>).)+)\]\]',
                             '<a id="inside" href="' + inter[0][0] + inter_data[1] + '">' + inter_view + see_link + '</a>',
                             data,
                             1
                         )
                     else:
                         data = re.sub(
-                            '\[\[((?:(?!\[\[|\]\]).)+)\]\]',
+                            '\[\[((?:(?!\[\[|\]\]|<\/td>).)+)\]\]',
                             '<a id="inside" href="' + inter[0][0] + inter_data[1] + '">' + inter_view + inter_data[1] + '</a>',
                             data,
                             1
                         )
                 else:
-                    data = re.sub('\[\[((?:(?!\[\[|\]\]).)+)\]\]', 'Not exist', data, 1)
+                    data = re.sub('\[\[((?:(?!\[\[|\]\]|<\/td>).)+)\]\]', 'Not exist', data, 1)
             elif re.search('^(\/(?:.+))$', main_link):
                 under_title = re.search('^(\/(?:.+))$', main_link)
                 under_title = under_title.groups()[0]
 
                 if see_link != main_link:
-                    data = re.sub('\[\[((?:(?!\[\[|\]\]).)+)\]\]', '[[' + title + under_title + '|' + see_link + ']]', data, 1)
+                    data = re.sub('\[\[((?:(?!\[\[|\]\]|<\/td>).)+)\]\]', '[[' + title + under_title + '|' + see_link + ']]', data, 1)
                 else:
-                    data = re.sub('\[\[((?:(?!\[\[|\]\]).)+)\]\]', '[[' + title + under_title + ']]', data, 1)
+                    data = re.sub('\[\[((?:(?!\[\[|\]\]|<\/td>).)+)\]\]', '[[' + title + under_title + ']]', data, 1)
             elif re.search('^http(s)?:\/\/', main_link):
-                data = re.sub('\[\[((?:(?!\[\[|\]\]).)+)\]\]', '<a id="out_link" rel="nofollow" href="' + main_link + '">' + see_link + '</a>', data, 1)
+                data = re.sub('\[\[((?:(?!\[\[|\]\]|<\/td>).)+)\]\]', '<a id="out_link" rel="nofollow" href="' + main_link + '">' + see_link + '</a>', data, 1)
             else:
                 return_link = link_fix(main_link)
                 main_link = html.unescape(return_link[0])
@@ -1041,9 +1100,9 @@ def namumark(conn, data, title, main_num, include_num):
                                 backlink += [[title, main_link, 'no']]
 
                             data = re.sub(
-                                '\[\[((?:(?!\[\[|\]\]).)+)\]\]',
+                                '\[\[((?:(?!\[\[|\]\]|<\/td>).)+)\]\]',
                                 '<a class="' + include_num + 'link_finder" ' + \
-                                    'title="' + main_link + other_link + '" ' + \
+                                    'title="' + html.escape(main_link) + other_link + '" ' + \
                                     'href="/w/' + tool.url_pas(main_link) + other_link + '"' + \
                                 '>' + see_link + '</a>',
                                 data,
@@ -1051,7 +1110,7 @@ def namumark(conn, data, title, main_num, include_num):
                             )
                         else:
                             data = re.sub(
-                                '\[\[((?:(?!\[\[|\]\]).)+)\]\]',
+                                '\[\[((?:(?!\[\[|\]\]|<\/td>).)+)\]\]',
                                 '<a title="' + other_link + '" href="' + other_link + '">' + see_link + '</a>',
                                 data,
                                 1
@@ -1059,15 +1118,15 @@ def namumark(conn, data, title, main_num, include_num):
                     else:
                         if re.search('^#', other_link):
                             data = re.sub(
-                                '\[\[((?:(?!\[\[|\]\]).)+)\]\]',
+                                '\[\[((?:(?!\[\[|\]\]|<\/td>).)+)\]\]',
                                 '<a title="' + other_link + '" href="' + other_link + '">' + other_link + '</a>',
                                 data,
                                 1
                             )
                         else:
-                            data = re.sub('\[\[((?:(?!\[\[|\]\]).)+)\]\]', '<b>' + see_link + '</b>', data, 1)
+                            data = re.sub('\[\[((?:(?!\[\[|\]\]|<\/td>).)+)\]\]', '<b>' + see_link + '</b>', data, 1)
                 else:
-                    data = re.sub('\[\[((?:(?!\[\[|\]\]).)+)\]\]', '&#91;&#91;' + link + '&#93;&#93;', data, 1)
+                    data = re.sub('\[\[((?:(?!\[\[|\]\]|<\/td>).)+)\]\]', '&#91;&#91;' + link + '&#93;&#93;', data, 1)
         else:
             break
 
@@ -1200,33 +1259,6 @@ def namumark(conn, data, title, main_num, include_num):
 
     data += category
 
-    for i in end_data:
-        if end_data[i][1] == 'normal':
-            data = data.replace('<span id="' + i + '"></span>', end_data[i][0])
-            data = data.replace(tool.url_pas('<span id="' + i + '"></span>'), tool.url_pas(end_data[i][0]))
-        else:
-            if re.search('\n', end_data[i][0]):
-                data = data.replace('<span id="' + i + '"></span>', '\n<pre>' + re.sub('^\n', '', end_data[i][0]) + '</pre>')
-            else:
-                data = data.replace('<span id="' + i + '"></span>', '<code>' + end_data[i][0] + '</code>')
-
-    if main_num == 1:
-        i = 0
-        while 1:
-            try:
-                _ = backlink[i][0]
-            except:
-                break
-
-            find_data = re.findall('<span id="(one_nowiki_[0-9]+)">', backlink[i][1])
-            for j in find_data:
-                if backlink[i][2] != 'redirect':
-                    backlink[i][1] = backlink[i][1].replace('<span id="' + j + '"></span>', end_data[j][0])
-                else:
-                    backlink[i][1] = backlink[i][1].replace('<span id="' + j + '"></span>', '\\' + end_data[j][0])
-
-            i += 1
-
     data = re.sub('<\/td_1>', '</td>', data)
     data = re.sub('<\/ul>\n?', '</ul>', data)
     data = re.sub('<\/pre>\n?', '</pre>', data)

+ 1 - 4
route/tool/set_mark/tool.py

@@ -41,10 +41,7 @@ def savemark(data):
     data = re.sub("\[date\(now\)\]", get_time(), data)
 
     ip = ip_check()
-    if not re.search("\.|:", ip):
-        name = '[[user:' + ip + '|' + ip + ']]'
-    else:
-        name = ip
+    name = '[[user:' + ip + '|' + ip + ']]' if not re.search("\.|:", ip) else ip
 
     data = re.sub("\[name\]", name, data)
 

+ 27 - 28
route/topic.py

@@ -3,24 +3,25 @@ from .tool.func import *
 def topic_2(conn, topic_num):
     curs = conn.cursor()
 
+    admin = admin_check(3)
+    topic_num = str(topic_num)
+
     if flask.request.method == 'POST':
         name = flask.request.form.get('topic', 'test')
         sub = flask.request.form.get('title', 'test')
     else:
-        topic_change_data = topic_change(topic_num)
-        name = topic_change_data[0]
-        sub = topic_change_data[1]
-
-    ban = acl_check(name, 'topic', sub)
-    admin = admin_check(3)
+        curs.execute(db_change("select title, sub from rd where code = ?"), [topic_num])
+        name = curs.fetchall()
+        if name:
+            sub = name[0][1]
+            name = name[0][0]
+        else:
+            return redirect('/')
 
-    curs.execute(db_change("select id from topic where title = ? and sub = ? limit 1"), [name, sub])
-    topic_exist = curs.fetchall()
-    if not topic_exist and len(sub) > 256:
-        return re_error('/error/11')
+    ban = acl_check(name, 'topic', topic_num)
 
     if flask.request.method == 'POST':
-        if captcha_post(flask.request.form.get('g-recaptcha-response', '')) == 1:
+        if captcha_post(flask.request.form.get('g-recaptcha-response', flask.request.form.get('g-recaptcha', ''))) == 1:
             return re_error('/error/13')
         else:
             captcha_post('', 0)
@@ -31,13 +32,15 @@ def topic_2(conn, topic_num):
         if ban == 1:
             return re_error('/ban')
 
-        curs.execute(db_change("select id from topic where title = ? and sub = ? order by id + 0 desc limit 1"), [name, sub])
+        curs.execute(db_change("select id from topic where code = ? order by id + 0 desc limit 1"), [topic_num])
         old_num = curs.fetchall()
         if old_num:
             num = int(old_num[0][0]) + 1
         else:
             num = 1
 
+        num = str(num)
+
         match = re.search('^user:([^/]+)', name)
         if match:
             y_check = 0
@@ -60,7 +63,7 @@ def topic_2(conn, topic_num):
             if y_check == 1:
                 curs.execute(db_change('insert into alarm (name, data, date) values (?, ?, ?)'), [
                     match.groups()[0],
-                    ip + ' | <a href="/thread/' + str(topic_num) + '#' + str(num) + '">' + name + ' | ' + sub + ' | #' + str(num) + '</a>',
+                    ip + ' | <a href="/thread/' + topic_num + '#' + num + '">' + name + ' | ' + sub + ' | #' + num + '</a>',
                     today
                 ])
 
@@ -68,37 +71,33 @@ def topic_2(conn, topic_num):
         data = cate_re.sub('[br]', flask.request.form.get('content', 'Test'))
 
         for rd_data in re.findall("(?:#([0-9]+))", data):
-            curs.execute(db_change("select ip from topic where title = ? and sub = ? and id = ?"), [name, sub, rd_data])
+            curs.execute(db_change("select ip from topic where code = ? and id = ?"), [topic_num, rd_data])
             ip_data = curs.fetchall()
             if ip_data and ip_or_user(ip_data[0][0]) == 0:
                 curs.execute(db_change('insert into alarm (name, data, date) values (?, ?, ?)'), [
                     ip_data[0][0],
-                    ip + ' | <a href="/thread/' + str(topic_num) + '#' + str(num) + '">' + name + ' | ' + sub + ' | #' + str(num) + '</a>',
+                    ip + ' | <a href="/thread/' + topic_num + '#' + num + '">' + name + ' | ' + sub + ' | #' + num + '</a>',
                     today
                 ])
 
         data = re.sub("(?P<in>#(?:[0-9]+))", '[[\g<in>]]', data)
         data = savemark(data)
 
-        rd_plus(name, sub, today)
-        curs.execute(db_change("insert into topic (id, title, sub, data, date, ip, block, top, code) values (?, ?, ?, ?, ?, ?, '', '', ?)"), [
-            str(num),
-            name,
-            sub,
+        rd_plus(topic_num, today, name, sub)
+        curs.execute(db_change("insert into topic (id, data, date, ip, code) values (?, ?, ?, ?, ?)"), [
+            num,
             data,
             today,
             ip,
-            str(topic_num) if num == 1 else ''
+            topic_num
         ])
         conn.commit()
 
-        return redirect('/thread/' + str(topic_num) + '?where=bottom')
+        return redirect('/thread/' + topic_num + '?where=bottom')
     else:
         data = ''
 
-        curs.execute(db_change("select stop from rd where title = ? and sub = ? and stop != ''"), [name, sub])
-        close_data = curs.fetchall()
-        if (close_data and admin != 1) or ban == 1:
+        if ban == 1:
             display = 'display: none;'
         else:
             display = ''
@@ -107,8 +106,8 @@ def topic_2(conn, topic_num):
             <div id="top_topic"></div>
             <div id="main_topic"></div>
             <div id="plus_topic"></div>
-            <script>topic_top_load("''' + str(topic_num) + '''");</script>
-            <a href="/thread/''' + str(topic_num) + '/tool">(' + load_lang('topic_tool') + ''')</a>
+            <script>topic_top_load("''' + topic_num + '''");</script>
+            <a href="/thread/''' + topic_num + '/tool">(' + load_lang('topic_tool') + ''')</a>
             <hr class=\"main_hr\">
             <form style="''' + display + '''" method="post">
                 <textarea rows="10" id="content" placeholder="''' + load_lang('content') + '''" name="content"></textarea>
@@ -117,7 +116,7 @@ def topic_2(conn, topic_num):
                 <input style="display: none;" name="topic" value="''' + name + '''">
                 <input style="display: none;" name="title" value="''' + sub + '''">
                 <button type="submit">''' + load_lang('send') + '''</button>
-                <button id="preview" type="button" onclick="load_preview(\'''' + url_pas(name) + '\')">' + load_lang('preview') + '''</button>
+                <button id="preview" type="button" onclick="load_preview(\'\')">''' + load_lang('preview') + '''</button>
             </form>
             <hr class=\"main_hr\">
             <div id="see_preview"></div>

+ 70 - 0
route/topic_acl.py

@@ -0,0 +1,70 @@
+from .tool.func import *
+
+def topic_acl_2(conn, topic_num):
+    curs = conn.cursor()
+
+    if admin_check(3) != 1:
+        return re_error('/error/3')
+
+    ip = ip_check()
+    time = get_time()
+    topic_num = str(topic_num)
+
+    curs.execute(db_change("select title, sub from rd where code = ?"), [topic_num])
+    rd_d = curs.fetchall()
+    if not rd_d:
+        return redirect('/')
+
+    if flask.request.method == 'POST':
+        admin_check(3, 'topic_acl_set (code ' + topic_num + ')')
+
+        curs.execute(db_change("select id from topic where code = ? order by id + 0 desc limit 1"), [topic_num])
+        topic_check = curs.fetchall()
+        if topic_check:
+            acl_data = flask.request.form.get('acl', '')
+
+            curs.execute(db_change("update rd set acl = ? where code = ?"), [
+                acl_data,
+                topic_num
+            ])
+
+            curs.execute(db_change("insert into topic (id, data, date, ip, top, code) values (?, ?, ?, ?, '1', ?)"), [
+                str(int(topic_check[0][0]) + 1),
+                'acl change ' + acl_data,
+                time,
+                ip,
+                topic_num
+            ])
+
+            rd_plus(topic_num, time)
+
+        return redirect('/thread/' + topic_num)
+    else:
+        acl_list = get_acl_list()
+        acl_html_list = ''
+
+        curs.execute(db_change("select acl from rd where code = ?"), [topic_num])
+        topic_acl_get = curs.fetchall()
+        for data_list in acl_list:
+            if topic_acl_get and topic_acl_get[0][0] == data_list:
+                check = 'selected="selected"'
+            else:
+                check = ''
+
+            acl_html_list += '<option value="' + data_list + '" ' + check + '>' + (data_list if data_list != '' else 'normal') + '</option>'
+
+        return easy_minify(flask.render_template(skin_check(),
+            imp = [load_lang('topic_acl_setting'), wiki_set(), custom(), other2([0, 0])],
+            data = '''
+                <form method="post">
+                    <a href="/acl/TEST#exp">(''' + load_lang('reference') + ''')</a>
+                    <hr>
+                    <select name="acl">
+                    ''' + acl_html_list + '''
+                    </select>
+                    <hr class=\"main_hr\">
+                    <button type="submit">''' + load_lang('save') + '''</button>
+                </form>
+            ''',
+            menu = [['thread/' + topic_num + '/tool', load_lang('return')]]
+        ))

+ 10 - 11
route/topic_admin.py

@@ -3,14 +3,13 @@ from .tool.func import *
 def topic_admin_2(conn, topic_num, num):
     curs = conn.cursor()
 
-    topic_change_data = topic_change(topic_num)
-    name = topic_change_data[0]
-    sub = topic_change_data[1]
+    num = str(num)
+    topic_num = str(topic_num)
 
-    curs.execute(db_change("select block, ip, date from topic where title = ? and sub = ? and id = ?"), [name, sub, str(num)])
+    curs.execute(db_change("select block, ip, date from topic where code = ? and id = ?"), [topic_num, num])
     data = curs.fetchall()
     if not data:
-        return redirect('/thread/' + str(topic_num))
+        return redirect('/thread/' + topic_num)
 
     ban = '''
         <h2>''' + load_lang('state') + '''</h2>
@@ -22,13 +21,13 @@ def topic_admin_2(conn, topic_num, num):
         <h2>''' + load_lang('other_tool') + '''</h2>
         <ul>
             <li>
-                <a href="/thread/''' + str(topic_num) + '/raw/' + str(num) + '''">''' + load_lang('raw') + '''</a>
+                <a href="/thread/''' + topic_num + '/raw/' + num + '''">''' + load_lang('raw') + '''</a>
             </li>
         </ul>
     '''
 
     if admin_check(3) == 1:
-        curs.execute(db_change("select id from topic where title = ? and sub = ? and id = ? and top = 'O'"), [name, sub, str(num)])
+        curs.execute(db_change("select id from topic where code = ? and id = ? and top = 'O'"), [topic_num, num])
         top_topic_d = curs.fetchall()
 
         curs.execute(db_change("select end from ban where block = ?"), [data[0][1]])
@@ -44,12 +43,12 @@ def topic_admin_2(conn, topic_num, num):
                     </a>
                 </li>
                 <li>
-                    <a href="/thread/''' + str(topic_num) + '/b/' + str(num) + '''">
+                    <a href="/thread/''' + topic_num + '/b/' + num + '''">
                         ''' + (load_lang('hide_release') if data[0][0] == 'O' else load_lang('hide')) + '''
                     </a>
                 </li>
                 <li>
-                    <a href="/thread/''' + str(topic_num) + '/notice/' + str(num) + '''">
+                    <a href="/thread/''' + topic_num + '/notice/' + num + '''">
                         ''' + (load_lang('pinned_release') if top_topic_d else load_lang('pinned')) + '''
                     </a>
                 </li>
@@ -57,7 +56,7 @@ def topic_admin_2(conn, topic_num, num):
         '''
 
     return easy_minify(flask.render_template(skin_check(),
-        imp = [load_lang('discussion_tool'), wiki_set(), custom(), other2([' (#' + str(num) + ')', 0])],
+        imp = [load_lang('discussion_tool'), wiki_set(), custom(), other2([' (#' + num + ')', 0])],
         data = ban,
-        menu = [['thread/' + str(topic_num) + '#' + str(num), load_lang('return')]]
+        menu = [['thread/' + topic_num + '#' + num, load_lang('return')]]
     ))

+ 8 - 9
route/topic_block.py

@@ -3,23 +3,22 @@ from .tool.func import *
 def topic_block_2(conn, topic_num, num):
     curs = conn.cursor()
 
-    topic_change_data = topic_change(topic_num)
-    name = topic_change_data[0]
-    sub = topic_change_data[1]
+    topic_num = str(topic_num)
+    num = str(num)
 
-    if admin_check(3, 'blind (' + name + ' - ' + sub + '#' + str(num) + ')') != 1:
+    if admin_check(3, 'blind (code ' + topic_num + '#' + num + ')') != 1:
         return re_error('/error/3')
 
-    curs.execute(db_change("select block from topic where title = ? and sub = ? and id = ?"), [name, sub, str(num)])
+    curs.execute(db_change("select block from topic where code = ? and id = ?"), [topic_num, num])
     block = curs.fetchall()
     if block:
         if block[0][0] == 'O':
-            curs.execute(db_change("update topic set block = '' where title = ? and sub = ? and id = ?"), [name, sub, str(num)])
+            curs.execute(db_change("update topic set block = '' where code = ? and id = ?"), [topic_num, num])
         else:
-            curs.execute(db_change("update topic set block = 'O' where title = ? and sub = ? and id = ?"), [name, sub, str(num)])
+            curs.execute(db_change("update topic set block = 'O' where code = ? and id = ?"), [topic_num, num])
 
-        rd_plus(name, sub, get_time())
+        rd_plus(topic_num, get_time())
 
         conn.commit()
 
-    return redirect('/thread/' + str(topic_num) + '#' + str(num))
+    return redirect('/thread/' + topic_num + '#' + num)

+ 61 - 0
route/topic_change.py

@@ -0,0 +1,61 @@
+from .tool.func import *
+
+def topic_change_2(conn, topic_num):
+    curs = conn.cursor()
+
+    if admin_check(None) != 1:
+        return re_error('/error/3')
+
+    ip = ip_check()
+    time = get_time()
+    topic_num = str(topic_num)
+
+    curs.execute(db_change("select title, sub from rd where code = ?"), [topic_num])
+    rd_d = curs.fetchall()
+    if not rd_d:
+        return redirect('/')
+
+    if flask.request.method == 'POST':
+        admin_check(None, 'move_topic (code ' + topic_num + ')')
+
+        curs.execute(db_change("select id from topic where code = ? order by id + 0 desc limit 1"), [topic_num])
+        topic_check = curs.fetchall()
+        if topic_check:
+            title_d = flask.request.form.get('title', 'test')
+            sub_d = flask.request.form.get('sub', 'test')
+
+            curs.execute(db_change("update rd set title = ?, sub = ? where code = ?"), [
+                title_d,
+                sub_d,
+                topic_num
+            ])
+
+            curs.execute(db_change("insert into topic (id, data, date, ip, top, code) values (?, ?, ?, ?, '1', ?)"), [
+                str(int(topic_check[0][0]) + 1),
+                'change name to ' + sub_d + '(' + title_d + ')',
+                time,
+                ip,
+                topic_num
+            ])
+
+            rd_plus(topic_num, time)
+
+        return redirect('/thread/' + topic_num)
+    else:
+        return easy_minify(flask.render_template(skin_check(),
+            imp = [load_lang('topic_name_change'), wiki_set(), custom(), other2([0, 0])],
+            data = '''
+                <form method="post">
+                    ''' + load_lang('document_name') + '''
+                    <hr class=\"main_hr\">
+                    <input value="''' + rd_d[0][0] + '''" name="title" type="text">
+                    <hr class=\"main_hr\">
+                    ''' + load_lang('discussion_name') + '''
+                    <hr class=\"main_hr\">
+                    <input value="''' + rd_d[0][1] + '''" name="sub" type="text">
+                    <hr class=\"main_hr\">
+                    <button type="submit">''' + load_lang('save') + '''</button>
+                </form>
+            ''',
+            menu = [['thread/' + topic_num + '/tool', load_lang('return')]]
+        ))

+ 18 - 28
route/topic_close_list.py

@@ -10,18 +10,18 @@ def topic_close_list_2(conn, name):
     menu = [['topic/' + url_pas(name), load_lang('return')]]
 
     if tool == 'close':
-        curs.execute(db_change("select sub from rd where title = ? and stop = 'O' order by sub asc"), [name])
+        curs.execute(db_change("select code, sub from rd where title = ? and stop = 'O' order by sub asc"), [name])
 
         sub = load_lang('closed_discussion')
     elif tool == 'agree':
-        curs.execute(db_change("select sub from rd where title = ? and agree = 'O' order by sub asc"), [name])
+        curs.execute(db_change("select code, sub from rd where title = ? and agree = 'O' order by sub asc"), [name])
 
         sub = load_lang('agreed_discussion')
     else:
         sub = load_lang('discussion_list')
         menu = [['w/' + url_pas(name), load_lang('document')]]
 
-        if acl_check(name, 'topic', sub) == 1:
+        if acl_check(name, 'topic', None) == 1:
             display = 'display: none;'
         else:
             display = ''
@@ -50,37 +50,27 @@ def topic_close_list_2(conn, name):
             <div id="see_preview"></div>
         '''
 
-        curs.execute(db_change("select sub from rd where title = ? order by date desc"), [name])
+        curs.execute(db_change("select code, sub from rd where title = ? and stop != 'O' order by date desc"), [name])
 
     t_num = 0
     for data in curs.fetchall():
         t_num += 1
+        t_str_num = str(t_num)
 
-        curs.execute(db_change("select code from topic where title = ? and sub = ? and id = '1'"), [name, data[0]])
-        first_topic = curs.fetchall()
-        if first_topic:
-            it_p = 0
-
-            if tool == '':
-                curs.execute(db_change("select title from rd where title = ? and sub = ? and stop = 'O' order by sub asc"), [name, data[0]])
-                if curs.fetchall():
-                    it_p = 1
-
-            if it_p != 1:
-                curs.execute(db_change("select id from topic where title = ? and sub = ? order by date desc limit 1"), [name, data[0]])
-                t_data = curs.fetchall()
+        curs.execute(db_change("select id from topic where code = ? order by date desc limit 1"), [data[0]])
+        t_data = curs.fetchall()
 
-                div += '''
-                    <h2><a href="/thread/''' + first_topic[0][0] + '">' + str(t_num) + '. ' + data[0] + '''</a></h2>
-                    <div id="topic_pre_''' + str(t_num) + '''"></div>
-                    <div id="topic_back_pre_''' + str(t_num) + '''"></div>
-                    <script>
-                        topic_list_load(''' + first_topic[0][0] + ', 1, "topic_pre_' + str(t_num) + '''");
-                        if(''' + str(t_data[0][0]) + ''' !== 1) {
-                            topic_list_load(''' + first_topic[0][0] + ', ' + t_data[0][0] + ', "topic_back_pre_' + str(t_num) + '''");
-                        }
-                    </script>
-                '''
+        div += '''
+            <h2><a href="/thread/''' + data[0] + '">' + t_str_num + '. ' + data[1] + '''</a></h2>
+            <div id="topic_pre_''' + t_str_num + '''"></div>
+            <div id="topic_back_pre_''' + t_str_num + '''"></div>
+            <script>
+                topic_list_load(''' + data[0] + ', 1, "topic_pre_' + t_str_num + '''");
+                if(''' + t_data[0][0] + ''' !== 1) {
+                    topic_list_load(''' + data[0] + ', ' + t_data[0][0] + ', "topic_back_pre_' + t_str_num + '''");
+                }
+            </script>
+        '''
 
     if div == '':
         plus = re.sub('^<br>', '', plus)

+ 4 - 6
route/topic_delete.py

@@ -6,13 +6,11 @@ def topic_delete_2(conn, topic_num):
     if admin_check(None) != 1:
         return re_error('/error/3')
 
-    topic_change_data = topic_change(topic_num)
-    name = topic_change_data[0]
-    sub = topic_change_data[1]
+    topic_num = str(topic_num)
 
     if flask.request.method == 'POST':
-        curs.execute(db_change("delete from topic where title = ? and sub = ?"), [name, sub])
-        curs.execute(db_change("delete from rd where title = ? and sub = ?"), [name, sub])
+        curs.execute(db_change("delete from topic where code = ?"), [topic_num])
+        curs.execute(db_change("delete from rd where code = ?"), [topic_num])
         conn.commit()
 
         return redirect('/topic/' + url_pas(name))
@@ -25,5 +23,5 @@ def topic_delete_2(conn, topic_num):
                     <button type="submit">''' + load_lang('start') + '''</button>
                 </form>
             ''',
-            menu = [['thread/' + str(topic_num) + '/tool', load_lang('return')]]
+            menu = [['thread/' + topic_num + '/tool', load_lang('return')]]
         ))

+ 27 - 39
route/topic_stop.py

@@ -8,24 +8,27 @@ def topic_stop_2(conn, topic_num):
 
     ip = ip_check()
     time = get_time()
+    topic_num = str(topic_num)
 
-    topic_change_data = topic_change(topic_num)
-    name = topic_change_data[0]
-    sub = topic_change_data[1]
+    curs.execute(db_change("select stop, agree from rd where code = ?"), [topic_num])
+    rd_d = curs.fetchall()
+    if not rd_d:
+        return redirect('/')
 
     if flask.request.method == 'POST':
-        curs.execute(db_change("select id from topic where title = ? and sub = ? order by id + 0 desc limit 1"), [name, sub])
+        admin_check(3, 'change_topic_set (code ' + topic_num + ')')
+
+        curs.execute(db_change("select id from topic where code = ? order by id + 0 desc limit 1"), [topic_num])
         topic_check = curs.fetchall()
         if topic_check:
             stop_d = flask.request.form.get('stop_d', '')
             why_d = flask.request.form.get('why', '')
             agree_d = flask.request.form.get('agree', '')
 
-            curs.execute(db_change("update rd set stop = ?, agree = ? where title = ? and sub = ?"), [
+            curs.execute(db_change("update rd set stop = ?, agree = ? where code = ?"), [
                 stop_d,
                 agree_d,
-                name,
-                sub
+                topic_num
             ])
 
             if stop_d == 'S':
@@ -35,52 +38,37 @@ def topic_stop_2(conn, topic_num):
             else:
                 t_state = 'Normal'
 
-            curs.execute(db_change("insert into topic (id, title, sub, data, date, ip, block, top) values (?, ?, ?, ?, ?, ?, '', '1')"), [
+            curs.execute(db_change("insert into topic (id, data, date, ip, top, code) values (?, ?, ?, ?, '1', ?)"), [
                 str(int(topic_check[0][0]) + 1),
-                name,
-                sub,
                 t_state + (' (Agree)' if agree_d != '' else '') + (('[br][br]Why : ' + why_d) if why_d else ''),
                 time,
-                ip
+                ip,
+                topic_num
             ])
 
-            rd_plus(name, sub, time)
+            rd_plus(topic_num, time)
 
-        return redirect('/thread/' + str(topic_num))
+        return redirect('/thread/' + topic_num)
     else:
         stop_d_list = ''
         agree_check = ''
+        for_list = [
+            ['O', 'Close'],
+            ['S', 'Stop'],
+            ['', 'Normal']
+        ]
 
-        curs.execute(db_change("select stop, agree from rd where title = ? and sub = ? limit 1"), [name, sub])
-        rd_d = curs.fetchall()
-        if rd_d[0][0] == 'O':
-            stop_d_list += '''
-                <option value="O">Close</option>
-                <option value="">Normal</option>
-                <option value="S">Stop</option>
-            '''
-        elif rd_d[0][0] == 'S':
-            stop_d_list += '''
-                <option value="S">Stop</option>
-                <option value="">Normal</option>
-                <option value="O">Close</option>
-            '''
-        else:
-            stop_d_list += '''
-                <option value="">Normal</option>
-                <option value="S">Stop</option>
-                <option value="O">Close</option>
-            '''
+        for i in for_list:
+            if rd_d and rd_d[0][0] == i[0]:
+                stop_d_list = '<option value="' + i[0] + '">' + i[1] + '</option>' + stop_d_list
+            else:
+                stop_d_list += '<option value="' + i[0] + '">' + i[1] + '</option>'
 
-        if rd_d[0][1] == 'O':
-            agree_check = 'checked="checked"'
-        else:
-            agree_check = ''
+        agree_check = 'checked="checked"' if rd_d[0][1] == 'O' else ''
 
         return easy_minify(flask.render_template(skin_check(),
             imp = [load_lang('topic_setting'), wiki_set(), custom(), other2([0, 0])],
             data = '''
-                <hr class=\"main_hr\">
                 <form method="post">
                     <select name="stop_d">
                         ''' + stop_d_list + '''
@@ -93,5 +81,5 @@ def topic_stop_2(conn, topic_num):
                     <button type="submit">''' + load_lang('save') + '''</button>
                 </form>
             ''',
-            menu = [['thread/' + str(topic_num) + '/tool', load_lang('return')]]
+            menu = [['thread/' + topic_num + '/tool', load_lang('return')]]
         ))

+ 15 - 9
route/topic_tool.py

@@ -4,12 +4,9 @@ def topic_tool_2(conn, topic_num):
     curs = conn.cursor()
 
     data = ''
+    topic_num = str(topic_num)
 
-    topic_change_data = topic_change(topic_num)
-    name = topic_change_data[0]
-    sub = topic_change_data[1]
-
-    curs.execute(db_change("select stop, agree from rd where title = ? and sub = ?"), [name, sub])
+    curs.execute(db_change("select stop, agree from rd where code = ?"), [topic_num])
     close_data = curs.fetchall()
     if close_data:
         if close_data[0][0] == 'S':
@@ -21,18 +18,22 @@ def topic_tool_2(conn, topic_num):
     else:
         t_state = 'Normal'
 
+    curs.execute(db_change("select acl from rd where code = ?"), [topic_num])
+    topic_acl_get = curs.fetchall()
+
     if admin_check(3) == 1:
         data = '''
             <h2>''' + load_lang('admin_tool') + '''</h2>
             <ul>
-                <li><a href="/thread/''' + str(topic_num) + '/setting">' + load_lang('topic_setting') + '''</a></li>
+                <li><a href="/thread/''' + topic_num + '/setting">' + load_lang('topic_setting') + '''</a></li>
+                <li><a href="/thread/''' + topic_num + '/acl">' + load_lang('topic_acl_setting') + '''</a></li>
             </ul>
         '''
     data += '''
         <h2>''' + load_lang('tool') + '''</h2>
         <ul>
-            <li><a id="reload" href="javascript:void(0);" onclick="req_alarm();">''' + load_lang('use_push_alarm') + '''</a></li>
             <li>''' + load_lang('topic_state') + ''' : ''' + t_state + '' + (' (Agree)' if close_data and (close_data[0][1] == 'O') else '') + '''</li>
+            <li>''' + load_lang('topic_acl') + ''' : <a href="/acl/TEST#exp">''' + ('Normal' if not topic_acl_get or (topic_acl_get[0][0] == '') else topic_acl_get[0][0]) + '''</a></li>
         </ul>
     '''
 
@@ -41,15 +42,20 @@ def topic_tool_2(conn, topic_num):
             <h2>''' + load_lang('owner') + '''</h2>
             <ul>
                 <li>
-                    <a href="/thread/''' + str(topic_num) + '''/delete">
+                    <a href="/thread/''' + topic_num + '''/delete">
                         ''' + load_lang('topic_delete') + '''
                     </a>
                 </li>
+                <li>
+                    <a href="/thread/''' + topic_num + '''/change">
+                        ''' + load_lang('topic_name_change') + '''
+                    </a>
+                </li>
             </ul>
         '''
 
     return easy_minify(flask.render_template(skin_check(),
         imp = [load_lang('topic_tool'), wiki_set(), custom(), other2([0, 0])],
         data = data,
-        menu = [['thread/' + str(topic_num), load_lang('return')]]
+        menu = [['thread/' + topic_num, load_lang('return')]]
     ))

+ 9 - 11
route/topic_top.py

@@ -3,25 +3,23 @@ from .tool.func import *
 def topic_top_2(conn, topic_num, num):
     curs = conn.cursor()
 
-    topic_change_data = topic_change(topic_num)
-    name = topic_change_data[0]
-    sub = topic_change_data[1]
+    topic_num = str(topic_num)
+    num = str(num)
 
-    if admin_check(3, 'notice (' + name + ' - ' + sub + '#' + str(num) + ')') != 1:
+    if admin_check(3, 'notice (code ' + topic_num + '#' + num + ')') != 1:
         return re_error('/error/3')
 
-    curs.execute(db_change("select title from topic where title = ? and sub = ? and id = ?"), [name, sub, str(num)])
+    curs.execute(db_change("select code from topic where code = ? and id = ?"), [topic_num, num])
     if curs.fetchall():
-        curs.execute(db_change("select top from topic where id = ? and title = ? and sub = ?"), [str(num), name, sub])
+        curs.execute(db_change("select top from topic where code = ? and id = ?"), [topic_num, num])
         top_data = curs.fetchall()
         if top_data:
             if top_data[0][0] == 'O':
-                curs.execute(db_change("update topic set top = '' where title = ? and sub = ? and id = ?"), [name, sub, str(num)])
+                curs.execute(db_change("update topic set top = '' where code = ? and id = ?"), [topic_num, num])
             else:
-                curs.execute(db_change("update topic set top = 'O' where title = ? and sub = ? and id = ?"), [name, sub, str(num)])
-
-        rd_plus(name, sub, get_time())
+                curs.execute(db_change("update topic set top = 'O' where code = ? and id = ?"), [topic_num, num])
 
+        rd_plus(topic_num, get_time())
         conn.commit()
 
-    return redirect('/thread/' + str(topic_num) + '#' + str(num))
+    return redirect('/thread/' + topic_num + '#' + num)

+ 7 - 7
route/user_count_edit.py

@@ -15,7 +15,7 @@ def user_count_edit_2(conn, name):
     else:
         data = 0
 
-    curs.execute(db_change("select count(title) from topic where ip = ?"), [that])
+    curs.execute(db_change("select count(code) from topic where ip = ?"), [that])
     count = curs.fetchall()
     if count:
         t_data = count[0][0]
@@ -24,11 +24,11 @@ def user_count_edit_2(conn, name):
 
     return easy_minify(flask.render_template(skin_check(),
         imp = [load_lang('count'), wiki_set(), custom(), other2([0, 0])],
-        data =  '''
-                <ul>
-                    <li><a href="/record/''' + url_pas(that) + '''">''' + load_lang('edit_record') + '''</a> : ''' + str(data) + '''</li>
-                    <li><a href="/topic_record/''' + url_pas(that) + '''">''' + load_lang('discussion_record') + '''</a> : ''' + str(t_data) + '''</a></li>
-                </ul>
-                ''',
+        data = '''
+            <ul>
+                <li><a href="/record/''' + url_pas(that) + '''">''' + load_lang('edit_record') + '''</a> : ''' + str(data) + '''</li>
+                <li><a href="/topic_record/''' + url_pas(that) + '''">''' + load_lang('discussion_record') + '''</a> : ''' + str(t_data) + '''</a></li>
+            </ul>
+        ''',
         menu = [['user', load_lang('return')]]
     ))

+ 15 - 17
route/view_raw.py

@@ -6,42 +6,40 @@ def view_raw_2(conn, name, topic_num, num):
     if acl_check(name, 'render') == 1:
         return re_error('/ban')
 
-    sub_title = None
     if topic_num:
-        topic_change_data = topic_change(topic_num)
-        name = topic_change_data[0]
-        sub_title = topic_change_data[1]
-
-    v_name = name
-    sub = ' (' + load_lang('raw') + ')'
+        topic_num = str(topic_num)
 
     if not num:
         num = flask.request.args.get('num', None)
         if num:
             num = int(number_check(num))
 
-    if not sub_title and num:
-        curs.execute(db_change("select title from history where title = ? and id = ? and hide = 'O'"), [name, str(num)])
+    v_name = name
+    num = str(num)
+    sub = ' (' + load_lang('raw') + ')'
+
+    if not topic_num and num:
+        curs.execute(db_change("select title from history where title = ? and id = ? and hide = 'O'"), [name, num])
         if curs.fetchall() and admin_check(6) != 1:
             return re_error('/error/3')
 
-        curs.execute(db_change("select data from history where title = ? and id = ?"), [name, str(num)])
+        curs.execute(db_change("select data from history where title = ? and id = ?"), [name, num])
 
-        sub += ' (r' + str(num) + ')'
+        sub += ' (r' + num + ')'
 
         menu = [['history/' + url_pas(name), load_lang('history')]]
-    elif sub_title:
+    elif topic_num:
         if admin_check(6) != 1:
-            curs.execute(db_change("select data from topic where id = ? and title = ? and sub = ? and block = ''"), [str(num), name, sub_title])
+            curs.execute(db_change("select data from topic where id = ? and code = ? and block = ''"), [num, topic_num])
         else:
-            curs.execute(db_change("select data from topic where id = ? and title = ? and sub = ?"), [str(num), name, sub_title])
+            curs.execute(db_change("select data from topic where id = ? and code = ?"), [num, topic_num])
 
         v_name = load_lang('discussion_raw')
-        sub = ' (#' + str(num) + ')'
+        sub = ' (#' + num + ')'
 
         menu = [
-            ['topic/' + url_pas(name) + '/sub/' + url_pas(sub_title) + '#' + str(num), load_lang('discussion')], 
-            ['topic/' + url_pas(name) + '/sub/' + url_pas(sub_title) + '/admin/' + str(num), load_lang('return')]
+            ['thread/' + topic_num + '#' + num, load_lang('discussion')], 
+            ['thread/' + topic_num + '/admin/' + num, load_lang('return')]
         ]
     else:
         curs.execute(db_change("select data from data where title = ?"), [name])

+ 21 - 21
route/view_read.py

@@ -28,7 +28,7 @@ def view_read_2(conn, name):
 
     curs.execute(db_change("select link from back where title = ? and type = 'cat' order by link asc"), [name])
 
-    curs.execute(db_change("select title from data where title like ?"), ['%' + name + '/%'])
+    curs.execute(db_change("select title from data where title >= '' and title like ?"), ['%' + name + '/%'])
     if curs.fetchall():
         down = 1
     else:
@@ -44,10 +44,12 @@ def view_read_2(conn, name):
         curs.execute(db_change("select link from back where title = ? and type = 'cat' order by link asc"), [name])
         back = curs.fetchall()
         if back:
-            div = '<br><h2 id="cate_normal">' + load_lang('category') + '</h2><ul>'
             u_div = ''
 
             for data in back:
+                if div == '':
+                    div = '<br><h2 id="cate_normal">' + load_lang('category_title') + '</h2><ul>'
+
                 if re.search('^category:', data[0]):
                     u_div += '<li><a href="/w/' + url_pas(data[0]) + '">' + data[0] + '</a></li>'
                 else:
@@ -58,10 +60,8 @@ def view_read_2(conn, name):
                     else:
                         div += '<li><a href="/w/' + url_pas(data[0]) + '">' + data[0] + '</a></li>'
 
-            div += '</ul>'
-
-            if div == '<br><h2 id="cate_normal">' + load_lang('category') + '</h2><ul></ul>':
-                div = ''
+            if div != '':
+                div += '</ul>'
 
             if u_div != '':
                 div += '<br><h2 id="cate_under">' + load_lang('under_category') + '</h2><ul>' + u_div + '</ul>'
@@ -94,11 +94,6 @@ def view_read_2(conn, name):
         else:
             else_data = None
 
-        curs.execute(db_change("select decu from acl where title = ?"), [name])
-        data = curs.fetchall()
-        if data:
-            acl = 1
-
         if flask.request.args.get('from', None) and else_data:
             else_data = re.sub('^\r\n', '', else_data)
             else_data = re.sub('\r\n$', '', else_data)
@@ -112,6 +107,11 @@ def view_read_2(conn, name):
             curs.execute(db_change("delete from cache_data where title = ?"), [name])
             if last_history_num:
                 curs.execute(db_change("insert into cache_data (title, data, id) values (?, ?, ?)"), [name, end_data, last_history_num[0][0]])
+                
+    curs.execute(db_change("select decu from acl where title = ?"), [name])
+    data = curs.fetchall()
+    if data:
+        acl = 1
 
     if end_data == 'HTTP Request 401.3':
         response_data = 401
@@ -132,7 +132,10 @@ def view_read_2(conn, name):
         else:
             end_data = '<h2>' + load_lang('error') + '</h2><ul><li>' + load_lang('decument_404_error') + '</li></ul>'
 
-        curs.execute(db_change('select ip, date, leng, send from history where title = ? order by id desc limit 3'), [name])
+        curs.execute(db_change('' + \
+            'select ip, date, leng, send, id from history ' + \
+            'where title = ? and hide != "O" and type = "" order by id desc limit 3' + \
+        ''), [name])
         sql_d = curs.fetchall()
         if sql_d:
             end_data += '<h2>' + load_lang('history') + '</h2><ul>'
@@ -144,7 +147,7 @@ def view_read_2(conn, name):
                 else:
                     leng = '<span style="color:gray;">(' + i[2] + ')</span>'
 
-                end_data += '<li>' + i[1] + ' | ' + ip_pas(i[0]) + ' | ' + leng + (' | ' + i[3] if i[3] != '' else '') + '</li>'
+                end_data += '<li>' + i[1] + ' | r' + i[4] + ' | ' + ip_pas(i[0]) + ' | ' + leng + (' | ' + i[3] if i[3] != '' else '') + '</li>'
 
             end_data += '<li><a href="/history/' + url_pas(name) + '">(...)</a></li></ul>'
     else:
@@ -193,15 +196,12 @@ def view_read_2(conn, name):
 
     div = end_data + div
 
-    adsense_code = '<div align="center" style="display: block; margin-bottom: 10px;">{}</div>'
-
     curs.execute(db_change("select data from other where name = 'adsense'"))
-    adsense_enabled = curs.fetchall()[0][0]
-    if adsense_enabled == 'True':
+    if curs.fetchall()[0][0] == 'True':
         curs.execute(db_change("select data from other where name = 'adsense_code'"))
-        adsense_code = adsense_code.format(curs.fetchall()[0][0])
+        adsense_code = '<div align="center" style="display: block;">' + curs.fetchall()[0][0] + '</div><hr class=\"main_hr\">'
     else:
-        adsense_code = adsense_code.format('')
+        adsense_code = ''
 
     div = adsense_code + '<div>' + div + '</div>'
 
@@ -216,12 +216,12 @@ def view_read_2(conn, name):
     curs.execute(db_change("select data from other where name = 'body'"))
     body = curs.fetchall()
     if body:
-        div = body[0][0] + '<hr class=\"main_hr\">' + div
+        div = body[0][0] + div
 
     curs.execute(db_change("select data from other where name = 'bottom_body'"))
     body = curs.fetchall()
     if body:
-        div += '<hr class=\"main_hr\">' + body[0][0]
+        div += body[0][0]
 
     if ip_or_user(ip) == 0:
         curs.execute(db_change("select title from scan where user = ? and title = ?"), [ip, name])

+ 6 - 6
version.json

@@ -1,11 +1,11 @@
 {
     "master" : {
-        "r_ver" : "v3.1.6-stable-05",
-        "c_ver" : "3160500",
-        "s_ver" : "7"
+        "r_ver" : "v3.1.7-stable-01",
+        "c_ver" : "3172800",
+        "s_ver" : "8"
     }, "stable" : {
-        "r_ver" : "v3.1.6-stable-05",
-        "c_ver" : "3160500",
-        "s_ver" : "7"
+        "r_ver" : "v3.1.7-stable-01",
+        "c_ver" : "3172800",
+        "s_ver" : "8"
     }
 }

+ 1 - 1
views/main_css/css/main.css

@@ -43,7 +43,7 @@ blockquote { background-image: url(/views/acme/img/quote.png); background-positi
 @media (max-height: 400px) { .foot_plus { height: 80%; top: calc(100% - 92%); }}
 .foot_in { overflow-y: scroll; height: calc(100% - 20px); }
 #origin { display: none; }
-.all_in_data { display: block; width: 100%; }
+.all_in_data { display: block; width: 100%; white-space: pre-wrap; }
 .table_safe { max-width: 100%; }
 .change_space { white-space: pre-line; }
 div#topic_scroll { max-height: 500px; overflow: scroll; -ms-overflow-style: none; scrollbar-width: none; }

+ 24 - 0
views/main_css/js/load_namumark.js

@@ -72,4 +72,28 @@ function load_include(title, name, p_data) {
             }
         }
     }
+}
+
+function page_count() {
+    var n_ver = document.getElementsByClassName('all_page_count');
+
+    var url = "/api/title_index";
+
+    var xhr = new XMLHttpRequest();
+    xhr.open("GET", url, true);
+    xhr.send(null);
+
+    xhr.onreadystatechange = function() {
+        if(this.readyState === 4 && this.status === 200) {
+            var i = 0;
+            while(1) {
+                if(n_ver[i]) {
+                    n_ver[i].innerHTML = JSON.parse(this.responseText)['count'];
+                    i += 1;
+                } else {
+                    break;
+                }
+            }
+        }
+    }
 }

+ 232 - 0
views/main_css/js/load_skin_set.js

@@ -0,0 +1,232 @@
+function main_css_get_post() {
+    var cookies = document.cookie;
+    
+    var check = document.getElementById('strike');
+    if(check.value === 'normal') {
+        document.cookie = 'main_css_del_strike=0;';
+    } else if(check.value === 'change') {
+        document.cookie = 'main_css_del_strike=1;';
+    } else {
+        document.cookie = 'main_css_del_strike=2;';
+    }
+
+    check = document.getElementById('bold');
+    if(check.value === 'normal') {
+        document.cookie = 'main_css_del_bold=0;';
+    } else if(check.value === 'change') {
+        document.cookie = 'main_css_del_bold=1;';
+    } else {
+        document.cookie = 'main_css_del_bold=2;';
+    }
+
+    check = document.getElementById('include');
+    if(check.checked === true) {
+        document.cookie = 'main_css_include_link=1;';
+    } else {
+        document.cookie = 'main_css_include_link=0;';
+    }
+
+    check = document.getElementById('category');
+    if(check.value === 'bottom') {
+        document.cookie = 'main_css_category_set=0;';
+    } else {
+        document.cookie = 'main_css_category_set=1;';
+    }
+
+    history.go(0);
+}
+
+function main_css_skin_load() {
+    var cookies = document.cookie;
+    
+    var head_data = document.querySelector('head');
+    if(cookies.match(main_css_regex_data('main_css_del_strike'))) {
+        if(cookies.match(main_css_regex_data('main_css_del_strike'))[1] === '1') {
+            head_data.innerHTML += '<style>s { text-decoration: none; } s:hover { background-color: transparent; }</style>';
+        } else if(cookies.match(main_css_regex_data('main_css_del_strike'))[1] === '2') {
+            head_data.innerHTML += '<style>s { display: none; }</style>';
+        }
+    }
+
+    if(cookies.match(main_css_regex_data('main_css_del_bold'))) {
+        if(cookies.match(main_css_regex_data('main_css_del_bold'))[1] === '1') {
+            head_data.innerHTML += '<style>b { font-weight: normal; }</style>';
+        } else if(cookies.match(main_css_regex_data('main_css_del_bold'))[1] === '2') {
+            head_data.innerHTML += '<style>b { display: none; }</style>';
+        }
+    }
+
+    if(
+        cookies.match(main_css_regex_data('main_css_include_link')) &&
+        cookies.match(main_css_regex_data('main_css_include_link'))[1] === '1'
+    ) {
+        head_data.innerHTML += '<style>#include_link { display: inline; }</style>';
+    }
+
+    if(
+        cookies.match(main_css_regex_data('main_css_category_set')) &&
+        cookies.match(main_css_regex_data('main_css_category_set'))[1] === '1'
+    ) {
+        var get_category = document.getElementById('cate_all');
+        if(get_category) {
+            var backup_category = get_category.innerHTML;
+            var in_data = document.getElementById('in_data_0').innerHTML;
+            get_category.innerHTML = '';
+
+            backup_category = backup_category.replace('<hr>', '') + '<hr>';
+
+            document.getElementById('in_data_0').innerHTML = backup_category + in_data;
+        }
+    }
+}
+
+function main_css_regex_data(data) {
+    return new RegExp('(?:^|; )' + data + '=([^;]*)');
+}
+
+function main_css_skin_set() {
+    var cookies = document.cookie;
+    
+    if(window.location.pathname === '/main_skin_set') {
+        var set_language = {
+            "en-US" : {
+                "default" : "Default",
+                "change_to_normal" : "Change to normal text",
+                "delete" : "Delete",
+                "include_link" : "Using include link",
+                "save" : "Save",
+                "strike" : "Strike",
+                "bold" : "Bold",
+                "other" : "Other",
+                "where_category" : "Set category location",
+                "bottom" : "Bottom",
+                "top" : "Top"
+            }, "ko-KR" : {
+                "default" : "기본값",
+                "change_to_normal" : "일반 텍스트로 변경",
+                "delete" : "삭제",
+                "include_link" : "틀 링크 사용",
+                "save" : "저장",
+                "strike" : "취소선",
+                "bold" : "볼드체",
+                "other" : "기타",
+                "where_category" : "분류 위치 설정",
+                "bottom" : "아래",
+                "top" : "위"
+            }
+        }
+
+        var language = cookies.match(main_css_regex_data('language'))[1];
+        var user_language = cookies.match(main_css_regex_data('user_language'))[1];
+        if(user_language in set_language) {
+            language = user_language;
+        }
+
+        if(!language in set_language) {
+            language = "en-US";
+        }
+
+        var data = document.getElementById("main_skin_set");
+        var set_data = {};
+
+        var strike_list = [
+            ['0', 'normal', set_language[language]['default']],
+            ['1', 'change', set_language[language]['change_to_normal']],
+            ['2', 'delete', set_language[language]['delete']]
+        ];
+        set_data["strike"] = '';
+        var i = 0;
+        while(1) {
+            if(strike_list[i]) {
+                if(
+                    cookies.match(main_css_regex_data('main_css_del_strike')) && 
+                    cookies.match(main_css_regex_data('main_css_del_strike'))[1] === strike_list[i][0]
+                ) {
+                    set_data["strike"] = '<option value="' + strike_list[i][1] + '">' + strike_list[i][2] + '</option>' + set_data["strike"];
+                } else {
+                    set_data["strike"] += '<option value="' + strike_list[i][1] + '">' + strike_list[i][2] + '</option>';
+                }
+
+                i += 1;
+            } else {
+                break;
+            }
+        }
+
+        var bold_list = [
+            ['0', 'normal', set_language[language]['default']],
+            ['1', 'change', set_language[language]['change_to_normal']],
+            ['2', 'delete', set_language[language]['delete']]
+        ];
+        set_data["bold"] = '';
+        var i = 0;
+        while(1) {
+            if(bold_list[i]) {
+                if(
+                    cookies.match(main_css_regex_data('main_css_del_bold')) && 
+                    cookies.match(main_css_regex_data('main_css_del_bold'))[1] === bold_list[i][0]
+                ) {
+                    set_data["bold"] = '<option value="' + bold_list[i][1] + '">' + bold_list[i][2] + '</option>' + set_data["bold"];
+                } else {
+                    set_data["bold"] += '<option value="' + bold_list[i][1] + '">' + bold_list[i][2] + '</option>';
+                }
+
+                i += 1;
+            } else {
+                break;
+            }
+        }
+
+        if(
+            cookies.match(main_css_regex_data('main_css_include_link')) &&
+            cookies.match(main_css_regex_data('main_css_include_link'))[1] === '1'
+        ) {
+            set_data["include"] = "checked";
+        }
+
+        var category_list = [
+            ['0', 'bottom', set_language[language]['bottom']],
+            ['1', 'top', set_language[language]['top']],
+        ];
+        set_data["category"] = '';
+        var i = 0;
+        while(1) {
+            if(category_list[i]) {
+                if(
+                    cookies.match(main_css_regex_data('main_css_category_set')) && 
+                    cookies.match(main_css_regex_data('main_css_category_set'))[1] === category_list[i][0]
+                ) {
+                    set_data["category"] = '<option value="' + category_list[i][1] + '">' + category_list[i][2] + '</option>' + set_data["category"];
+                } else {
+                    set_data["category"] += '<option value="' + category_list[i][1] + '">' + category_list[i][2] + '</option>';
+                }
+
+                i += 1;
+            } else {
+                break;
+            }
+        }
+
+
+        data.innerHTML = ' \
+            <h2>' + set_language[language]['strike'] + '</h2> \
+            <hr class="main_hr"> \
+            <select id="strike" name="strike"> \
+                ' + set_data["strike"] + ' \
+            </select> \
+            <h2>' + set_language[language]['bold'] + '</h2> \
+            <select id="bold" name="bold"> \
+                ' + set_data["bold"] + ' \
+            </select> \
+            <h2>' + set_language[language]['where_category'] + '</h2> \
+            <select id="category" name="category"> \
+                ' + set_data["category"] + ' \
+            </select> \
+            <hr class="main_hr"> \
+            <h2>' + set_language[language]['other'] + '</h2> \
+            <input ' + set_data["include"] + ' type="checkbox" id="include" name="include" value="include"> ' + set_language[language]['include_link'] + ' \
+            <hr class="main_hr"> \
+            <button onclick="main_css_get_post();">' + set_language[language]['save'] + '</button> \
+        ';
+    }
+}

+ 3 - 14
views/main_css/js/load_topic.js

@@ -49,22 +49,11 @@ function topic_plus_load(topic_num, num) {
                 p_data.innerHTML += n_data;
                 eval(t_plus_data);
 
-                // https://programmingsummaries.tistory.com/379
-                var options = {
-                    body: '#' + n_num
-                }
-
-                var notification = new Notification("openNAMU", options);
-
-                setTimeout(function () {
-                    notification.close();
-                }, 5000);
-
                 topic_plus_load(topic_num, String(Number(num) + 1));
                 clearInterval(test);
             }
         }
-    }, 2000);
+    }, 5000);
 }
 
 function topic_main_load(topic_num, s_num) {
@@ -82,8 +71,8 @@ function topic_main_load(topic_num, s_num) {
     xhr.send(null);
 
     xhr.onreadystatechange = function() {
-        if(xhr.readyState === 4 && xhr.status === 200) {
-            var t_data = JSON.parse(xhr.responseText);
+        if(this.readyState === 4 && this.status === 200) {
+            var t_data = JSON.parse(this.responseText);
             var t_plus_data = '';
 
             for(var key in t_data) {

+ 0 - 10
views/main_css/js/req_browser_alarm.js

@@ -1,10 +0,0 @@
-// https://programmingsummaries.tistory.com/379
-function req_alarm() {
-    Notification.requestPermission(function (result) {
-        if(result === 'denied') {
-            document.cookie = 'topic_req=false;';
-        } else {
-            document.cookie = 'topic_req=false;';
-        }
-    });
-}

+ 1 - 1
views/marisa/index.html

@@ -11,7 +11,7 @@
         <link rel="stylesheet" href="/views/marisa/css/main.css?ver=7">
         <script src="/views/marisa/js/skin_set.js?ver=3"></script>
         <script src="/views/marisa/js/main.js?ver=3"></script>
-        <script>window.onload = function () { skin_set(); main_load(); }</script>
+        <script>main_load(); window.onload = function () { skin_set(); }</script>
         <script src="https://code.iconify.design/1/1.0.3/iconify.min.js"></script>
         <link rel="shortcut icon" href="/views/main_css/file/favicon.ico?ver=1">
         {{imp[1][5]|safe}}

+ 2 - 2
views/marisa/info.json

@@ -1,5 +1,5 @@
 {
     "name" : "Marisa",
-    "skin_ver" : "v1.1.8",
-    "require_ver" : "7"
+    "skin_ver" : "v1.1.9",
+    "require_ver" : "8"
 }

+ 0 - 49
views/marisa/js/search.js

@@ -1,49 +0,0 @@
-function search_do() {
-    function search_load() {
-        var data = document.getElementById("search_input").value;
-        if(before !== data && data !== '') {
-            before = data;
-            var url = "/api/search/" + encodeURI(data);
-
-            var xhr = new XMLHttpRequest();
-            xhr.open("GET", url, true);
-            xhr.send(null);
-
-            xhr.onreadystatechange = function() {
-                if(this.readyState === 4 && this.status === 200) {
-                    document.getElementById("pre_search").style.display = 'block';
-
-                    var get_data = JSON.parse(this.responseText);
-                    document.getElementById("pre_search").innerHTML = '';
-
-                    if(this.responseText !== "{}\n") {
-                        for(key in get_data) {
-                            document.getElementById("pre_search").innerHTML += '<a href="/w/' + encodeURI(get_data[key][0]).replace('#', '%23') + '">' + get_data[key][0] + '</a><br>';
-                        }
-                    } else {
-                        document.getElementById("pre_search").style.display = 'none';
-                    }
-                }
-            }
-        } else if(before !== data && data === '') {
-            before = '';
-            document.getElementById("pre_search").style.display = 'none';
-        }
-    }
-
-    var before = '';
-    save_data = 'pre_search';
-    open = 1;
-    setTimeout(function() { open = 0; }, 100);
-    setInterval(search_load, 1000);
-}
-
-function view_search() {
-    var data = document.getElementById("pre_search").innerHTML;
-    if(data !== '') {
-        document.getElementById("pre_search").style.display = 'block';
-    }
-
-    open = 1;
-    setTimeout(function() { open = 0; }, 100);
-}

+ 2 - 189
views/marisa/js/skin_set.js

@@ -1,29 +1,4 @@
 function get_post() {
-    var check = document.getElementById('strike');
-    if(check.value === 'normal') {
-        document.cookie = 'del_strike=0;';
-    } else if(check.value === 'change') {
-        document.cookie = 'del_strike=1;';
-    } else {
-        document.cookie = 'del_strike=2;';
-    }
-
-    check = document.getElementById('bold');
-    if(check.value === 'normal') {
-        document.cookie = 'del_bold=0;';
-    } else if(check.value === 'change') {
-        document.cookie = 'del_bold=1;';
-    } else {
-        document.cookie = 'del_bold=2;';
-    }
-
-    check = document.getElementById('include');
-    if(check.checked === true) {
-        document.cookie = 'include_link=1;';
-    } else {
-        document.cookie = 'include_link=0;';
-    }
-
     check = document.getElementById('invert');
     if(check.checked === true) {
         document.cookie = 'invert=1;';
@@ -31,63 +6,17 @@ function get_post() {
         document.cookie = 'invert=0;';
     }
 
-    check = document.getElementById('category');
-    if(check.value === 'bottom') {
-        document.cookie = 'category_set=0;';
-    } else {
-        document.cookie = 'category_set=1;';
-    }
-
     history.go(0);
 }
 
 function main_load() {
     var head_data = document.querySelector('head');
-    if(cookies.match(regex_data('del_strike'))) {
-        if(cookies.match(regex_data('del_strike'))[1] === '1') {
-            head_data.innerHTML += '<style>s { text-decoration: none; } s:hover { background-color: transparent; }</style>';
-        } else if(cookies.match(regex_data('del_strike'))[1] === '2') {
-            head_data.innerHTML += '<style>s { display: none; }</style>';
-        }
-    }
-
-    if(cookies.match(regex_data('del_bold'))) {
-        if(cookies.match(regex_data('del_bold'))[1] === '1') {
-            head_data.innerHTML += '<style>b { font-weight: normal; }</style>';
-        } else if(cookies.match(regex_data('del_bold'))[1] === '2') {
-            head_data.innerHTML += '<style>b { display: none; }</style>';
-        }
-    }
-
-    if(
-        cookies.match(regex_data('include_link')) &&
-        cookies.match(regex_data('include_link'))[1] === '1'
-    ) {
-        head_data.innerHTML += '<style>#include_link { display: inline; }</style>';
-    }
-
     if(
         cookies.match(regex_data('invert')) &&
         cookies.match(regex_data('invert'))[1] === '1'
     ) {
         head_data.innerHTML += '<link rel="stylesheet" href="/views/marisa/css/dark.css?ver=5">';
     }
-
-    if(
-        cookies.match(regex_data('category_set')) &&
-        cookies.match(regex_data('category_set'))[1] === '1'
-    ) {
-        var get_category = document.getElementById('cate_all');
-        if(get_category) {
-            var backup_category = get_category.innerHTML;
-            var in_data = document.getElementById('in_data_0').innerHTML;
-            get_category.innerHTML = '';
-
-            backup_category = backup_category.replace('<hr>', '') + '<hr>';
-
-            document.getElementById('in_data_0').innerHTML = backup_category + in_data;
-        }
-    }
 }
 
 function regex_data(data) {
@@ -100,31 +29,11 @@ function skin_set() {
     if(window.location.pathname === '/skin_set') {
         var set_language = {
             "en-US" : {
-                "default" : "Default",
-                "change_to_normal" : "Change to normal text",
-                "delete" : "Delete",
-                "include_link" : "Using include link",
                 "save" : "Save",
-                "strike" : "Strike",
-                "bold" : "Bold",
-                "other" : "Other",
-                "darkmode" : "Darkmode",
-                "where_category" : "Set category location",
-                "bottom" : "Bottom",
-                "top" : "Top"
+                "darkmode" : "Darkmode"
             }, "ko-KR" : {
-                "default" : "기본값",
-                "change_to_normal" : "일반 텍스트로 변경",
-                "delete" : "삭제",
-                "include_link" : "틀 링크 사용",
                 "save" : "저장",
-                "strike" : "취소선",
-                "bold" : "볼드체",
-                "other" : "기타",
-                "darkmode" : "다크모드",
-                "where_category" : "분류 위치 설정",
-                "bottom" : "아래",
-                "top" : "위"
+                "darkmode" : "다크모드"
             }
         }
 
@@ -141,61 +50,6 @@ function skin_set() {
         var data = document.getElementById("main_skin_set");
         var set_data = {};
 
-        var strike_list = [
-            ['0', 'normal', set_language[language]['default']],
-            ['1', 'change', set_language[language]['change_to_normal']],
-            ['2', 'delete', set_language[language]['delete']]
-        ];
-        set_data["strike"] = '';
-        var i = 0;
-        while(1) {
-            if(strike_list[i]) {
-                if(
-                    cookies.match(regex_data('del_strike')) && 
-                    cookies.match(regex_data('del_strike'))[1] === strike_list[i][0]
-                ) {
-                    set_data["strike"] = '<option value="' + strike_list[i][1] + '">' + strike_list[i][2] + '</option>' + set_data["strike"];
-                } else {
-                    set_data["strike"] += '<option value="' + strike_list[i][1] + '">' + strike_list[i][2] + '</option>';
-                }
-
-                i += 1;
-            } else {
-                break;
-            }
-        }
-
-        var bold_list = [
-            ['0', 'normal', set_language[language]['default']],
-            ['1', 'change', set_language[language]['change_to_normal']],
-            ['2', 'delete', set_language[language]['delete']]
-        ];
-        set_data["bold"] = '';
-        var i = 0;
-        while(1) {
-            if(bold_list[i]) {
-                if(
-                    cookies.match(regex_data('del_bold')) && 
-                    cookies.match(regex_data('del_bold'))[1] === bold_list[i][0]
-                ) {
-                    set_data["bold"] = '<option value="' + bold_list[i][1] + '">' + bold_list[i][2] + '</option>' + set_data["bold"];
-                } else {
-                    set_data["bold"] += '<option value="' + bold_list[i][1] + '">' + bold_list[i][2] + '</option>';
-                }
-
-                i += 1;
-            } else {
-                break;
-            }
-        }
-
-        if(
-            cookies.match(regex_data('include_link')) &&
-            cookies.match(regex_data('include_link'))[1] === '1'
-        ) {
-            set_data["include"] = "checked";
-        }
-
         if(
             cookies.match(regex_data('invert')) &&
             cookies.match(regex_data('invert'))[1] === '1'
@@ -203,48 +57,7 @@ function skin_set() {
             set_data["invert"] = "checked";
         }
 
-        var category_list = [
-            ['0', 'bottom', set_language[language]['bottom']],
-            ['1', 'top', set_language[language]['top']],
-        ];
-        set_data["category"] = '';
-        var i = 0;
-        while(1) {
-            if(category_list[i]) {
-                if(
-                    cookies.match(regex_data('category_set')) && 
-                    cookies.match(regex_data('category_set'))[1] === category_list[i][0]
-                ) {
-                    set_data["category"] = '<option value="' + category_list[i][1] + '">' + category_list[i][2] + '</option>' + set_data["category"];
-                } else {
-                    set_data["category"] += '<option value="' + category_list[i][1] + '">' + category_list[i][2] + '</option>';
-                }
-
-                i += 1;
-            } else {
-                break;
-            }
-        }
-
-
         data.innerHTML = ' \
-            <h2>' + set_language[language]['strike'] + '</h2> \
-            <hr class="main_hr"> \
-            <select id="strike" name="strike"> \
-                ' + set_data["strike"] + ' \
-            </select> \
-            <h2>' + set_language[language]['bold'] + '</h2> \
-            <select id="bold" name="bold"> \
-                ' + set_data["bold"] + ' \
-            </select> \
-            <h2>' + set_language[language]['where_category'] + '</h2> \
-            <select id="category" name="category"> \
-                ' + set_data["category"] + ' \
-            </select> \
-            <hr class="main_hr"> \
-            <h2>' + set_language[language]['other'] + '</h2> \
-            <input ' + set_data["include"] + ' type="checkbox" id="include" name="include" value="include"> ' + set_language[language]['include_link'] + ' \
-            <hr class="main_hr"> \
             <input ' + set_data["invert"] + ' type="checkbox" id="invert" name="invert" value="invert"> ' + set_language[language]['darkmode'] + ' \
             <hr class="main_hr"> \
             <button onclick="get_post();">' + set_language[language]['save'] + '</button> \