Explorar el Código

Merge pull request #6 from hegelty/patch-1

매크로 문법 일부 수정
hegelty hace 4 años
padre
commit
d4973fb552
Se han modificado 56 ficheros con 1810 adiciones y 1324 borrados
  1. 2 0
      .gitignore
  2. 87 180
      app.py
  3. 2 98
      emergency_tool.py
  4. 13 1
      lang/en-US.json
  5. 15 3
      lang/ko-KR.json
  6. 3 2
      readme-en.md
  7. 2 1
      readme.md
  8. 21 0
      route/api_setting.py
  9. 220 218
      route/edit.py
  10. 41 0
      route/filter_document.py
  11. 62 0
      route/filter_document_add.py
  12. 13 0
      route/filter_document_delete.py
  13. 11 9
      route/filter_inter_wiki.py
  14. 12 12
      route/filter_inter_wiki_add.py
  15. 29 0
      route/filter_inter_wiki_delete.py
  16. 2 2
      route/give_user_check.py
  17. 0 29
      route/inter_wiki_del.py
  18. 28 35
      route/list_old_page.py
  19. 13 0
      route/login_find.py
  20. 116 0
      route/login_find_email.py
  21. 172 0
      route/login_find_email_check.py
  22. 56 0
      route/login_find_key.py
  23. 8 5
      route/login_login.py
  24. 0 115
      route/login_pass_find.py
  25. 0 164
      route/login_pass_find_email.py
  26. 2 2
      route/login_register_submit.py
  27. 2 2
      route/main_error_404.py
  28. 29 10
      route/main_func_setting.py
  29. 1 1
      route/main_func_skin_set.py
  30. 7 7
      route/main_tool_admin.py
  31. 1 1
      route/recent_history_tool.py
  32. 262 81
      route/tool/func.py
  33. 0 15
      route/tool/func_mark.py
  34. 20 10
      route/tool/func_tool.py
  35. 132 108
      route/topic.py
  36. 47 68
      route/topic_close_list.py
  37. 37 23
      route/user_alarm.py
  38. 7 6
      route/user_alarm_del.py
  39. 1 1
      route/user_info.py
  40. 8 2
      route/user_setting.py
  41. 4 0
      route/user_setting_email.py
  42. 4 0
      route/user_setting_email_check.py
  43. 12 0
      route/user_setting_email_delete.py
  44. 0 0
      route/user_setting_head.py
  45. 19 0
      route/user_setting_key.py
  46. 12 0
      route/user_setting_key_delete.py
  47. 6 7
      route/user_watch_list.py
  48. 2 0
      route/user_watch_list_name.py
  49. 2 1
      route/view_read.py
  50. 2 2
      version.json
  51. 5 6
      views/main_css/js/load_something.js
  52. 169 81
      views/main_css/js/render_onmark.js
  53. 6 5
      views/main_css/js/render_wiki.js
  54. 47 1
      views/ringo/css/main.css
  55. 33 7
      views/ringo/index.html
  56. 3 3
      views/tenshi/index.html

+ 2 - 0
.gitignore

@@ -12,6 +12,8 @@ images
 .vscode
 
 *.db
+*.db-shm
+*.db-wal
 *.db-journal
 
 robots.txt

+ 87 - 180
app.py

@@ -2,131 +2,36 @@
 import os
 import re
 
+from route.tool.func import *
+# from route import *
+
 for i_data in os.listdir("route"):
     f_src = re.search(r"(.+)\.py$", i_data)
-    if f_src:
-        exec("from route." + f_src.group(1) + " import *")
+    f_src = f_src.group(1) if f_src else ""
+    
+    if not f_src in ('', '__init__'):
+        try:
+            exec(
+                "from route." + f_src + " " + 
+                "import " + f_src
+            )
+        except:
+            try:
+                exec(
+                    "from route." + f_src + " " + 
+                    "import " + f_src + "_2"
+                )
+            except:
+                pass
 
 # Init-Version
-version_list = json.loads(open('version.json', encoding = 'utf8').read())
+version_list = json.loads(open(
+    'version.json', 
+    encoding = 'utf8'
+).read())
 
 # Init-DB
-if os.path.exists(os.path.join('data', 'set.json')):
-    db_set_list = ['db', 'db_type']
-    set_data = json.loads(open(
-        os.path.join('data', 'set.json'), 
-        encoding = 'utf8'
-    ).read())
-    for i in db_set_list:
-        if not i in set_data:
-            print('Please delete set.json')
-            print('----')
-            raise
-
-    print('DB name : ' + set_data['db'])
-    print('DB type : ' + set_data['db_type'])
-elif os.getenv('NAMU_DB') or os.getenv('NAMU_DB_TYPE'):
-    set_data = {}
-
-    if os.getenv('NAMU_DB'):
-        set_data['db'] = os.getenv('NAMU_DB')
-    else:
-        set_data['db'] = 'data'
-
-    if os.getenv('NAMU_DB_TYPE'):
-        set_data['db'] = os.getenv('NAMU_DB_TYPE')
-    else:
-        set_data['db'] = 'sqlite'
-
-    print('DB name : ' + set_data['db'])
-    print('DB type : ' + set_data['db_type'])
-else:
-    set_data = {}
-    normal_db_type = ['sqlite', 'mysql']
-
-    print('DB type (' + normal_db_type[0] + ') [' + ', '.join(normal_db_type) + '] : ', end = '')
-    data_get = str(input())
-    if data_get == '' or not data_get in normal_db_type:
-        set_data['db_type'] = 'sqlite'
-    else:
-        set_data['db_type'] = data_get
-
-    all_src = []
-    if set_data['db_type'] == 'sqlite':
-        for i_data in os.listdir("."):
-            f_src = re.search(r"(.+)\.db$", i_data)
-            if f_src:
-                all_src += [f_src.group(1)]
-
-    print('DB name (data) [' + ', '.join(all_src) + '] : ', end = '')
-    
-    data_get = str(input())
-    if data_get == '':
-        set_data['db'] = 'data'
-    else:
-        set_data['db'] = data_get
-        
-    with open(os.path.join('data', 'set.json'), 'w', encoding = 'utf8') as f:
-        f.write(json.dumps(set_data))
-
-data_db_set = {}
-data_db_set['name'] = set_data['db']
-data_db_set['type'] = set_data['db_type']
-
-if data_db_set['type'] == 'mysql':
-    if os.path.exists(os.path.join('data', 'mysql.json')):
-        db_set_list = ['user', 'password', 'host', 'port']
-        set_data = json.loads(
-            open(
-                os.path.join('data', 'mysql.json'),
-                encoding = 'utf8'
-            ).read()
-        )
-        for i in db_set_list:
-            if not i in set_data:
-                os.remove(os.path.join('data', 'mysql.json'))
-                
-                break
-                
-        set_data_mysql = set_data
-    
-    if not os.path.exists(os.path.join('data', 'mysql.json')):
-        set_data_mysql = {}
-        
-        print('DB user ID : ', end = '')
-        set_data_mysql['user'] = str(input())
-
-        print('DB password : ', end = '')
-        set_data_mysql['password'] = str(input())
-                
-        print('DB host (localhost) : ', end = '')
-        set_data_mysql['host'] = str(input())
-        if set_data_mysql['host'] == '':
-            set_data_mysql['host'] = 'localhost'
-
-        print('DB port (3306) : ', end = '')
-        set_data_mysql['port'] = str(input())
-        if set_data_mysql['port'] == '':
-            set_data_mysql['port'] = '3306'
-
-        with open(
-            os.path.join('data', 'mysql.json'), 
-            'w', 
-            encoding = 'utf8'
-        ) as f:
-            f.write(json.dumps(set_data_mysql))
-            
-    data_db_set['mysql_user'] = set_data_mysql['user']
-    data_db_set['mysql_pw'] = set_data_mysql['password']
-    if 'host' in set_data_mysql:
-        data_db_set['mysql_host'] = set_data_mysql['host']
-    else:
-        data_db_set['mysql_host'] = 'localhost'
-    
-    if 'port' in set_data_mysql:
-        data_db_set['mysql_port'] = set_data_mysql['port']
-    else:
-        data_db_set['mysql_port'] = '3306'
+data_db_set = class_check_json()
 
 db_data_get(data_db_set['type'])
 do_db_set(data_db_set)
@@ -220,7 +125,7 @@ app = flask.Flask(
 app.config['JSON_AS_ASCII'] = False
 app.config['JSONIFY_PRETTYPRINT_REGULAR'] = True
 
-log = logging.getLogger('werkzeug')
+log = logging.getLogger('waitress')
 log.setLevel(logging.ERROR)
 
 app.jinja_env.filters['md5_replace'] = md5_replace
@@ -347,44 +252,48 @@ if os.path.exists('custom.py'):
     
 # Func
 # Func-inter_wiki
-app.route('/inter_wiki', defaults = { 'tool' : 'inter_wiki' })(inter_wiki)
-app.route('/inter_wiki/del/<name>', defaults = { 'tool' : 'del_inter_wiki' })(inter_wiki_del)
-app.route('/inter_wiki/add', methods = ['POST', 'GET'], defaults = { 'tool' : 'plus_inter_wiki' })(inter_wiki_add)
+app.route('/inter_wiki', defaults = { 'tool' : 'inter_wiki' })(filter_inter_wiki)
+app.route('/inter_wiki/del/<name>', defaults = { 'tool' : 'del_inter_wiki' })(filter_inter_wiki_delete)
+app.route('/inter_wiki/add', methods = ['POST', 'GET'], defaults = { 'tool' : 'plus_inter_wiki' })(filter_inter_wiki_add)
+app.route('/inter_wiki/add/<name>', methods = ['POST', 'GET'], defaults = { 'tool' : 'plus_inter_wiki' })(filter_inter_wiki_add)
+
+app.route('/filter/document/list')(filter_document)
+app.route('/filter/document/add/<name>', methods = ['POST', 'GET'])(filter_document_add)
+app.route('/filter/document/add', methods = ['POST', 'GET'])(filter_document_add)
+app.route('/filter/document/del/<name>')(filter_document_delete)
 
-app.route('/edit_top', defaults = { 'tool' : 'edit_top' })(inter_wiki)
-app.route('/edit_top/del/<name>', defaults = { 'tool' : 'del_edit_top' })(inter_wiki_del)
-app.route('/edit_top/add', methods = ['POST', 'GET'], defaults = { 'tool' : 'plus_edit_top' })(inter_wiki_add)
+app.route('/edit_top', defaults = { 'tool' : 'edit_top' })(filter_inter_wiki)
+app.route('/edit_top/del/<name>', defaults = { 'tool' : 'del_edit_top' })(filter_inter_wiki_delete)
+app.route('/edit_top/add', methods = ['POST', 'GET'], defaults = { 'tool' : 'plus_edit_top' })(filter_inter_wiki_add)
 
-app.route('/image_license', defaults = { 'tool' : 'image_license' })(inter_wiki)
-app.route('/image_license/del/<name>', defaults = { 'tool' : 'del_image_license' })(inter_wiki_del)
-app.route('/image_license/add', methods = ['POST', 'GET'], defaults = { 'tool' : 'plus_image_license' })(inter_wiki_add)
+app.route('/image_license', defaults = { 'tool' : 'image_license' })(filter_inter_wiki)
+app.route('/image_license/del/<name>', defaults = { 'tool' : 'del_image_license' })(filter_inter_wiki_delete)
+app.route('/image_license/add', methods = ['POST', 'GET'], defaults = { 'tool' : 'plus_image_license' })(filter_inter_wiki_add)
 
-app.route('/edit_filter', defaults = { 'tool' : 'edit_filter' })(inter_wiki)
-app.route('/edit_filter/del/<name>', defaults = { 'tool' : 'del_edit_filter' })(inter_wiki_del)
-app.route('/edit_filter/add', methods = ['POST', 'GET'], defaults = { 'tool' : 'plus_edit_filter' })(inter_wiki_add)
-app.route('/edit_filter/add/<name>', methods = ['POST', 'GET'], defaults = { 'tool' : 'plus_edit_filter' })(inter_wiki_add)
+app.route('/edit_filter', defaults = { 'tool' : 'edit_filter' })(filter_inter_wiki)
+app.route('/edit_filter/del/<name>', defaults = { 'tool' : 'del_edit_filter' })(filter_inter_wiki_delete)
+app.route('/edit_filter/add', methods = ['POST', 'GET'], defaults = { 'tool' : 'plus_edit_filter' })(filter_inter_wiki_add)
+app.route('/edit_filter/add/<name>', methods = ['POST', 'GET'], defaults = { 'tool' : 'plus_edit_filter' })(filter_inter_wiki_add)
 
-app.route('/email_filter', defaults = { 'tool' : 'email_filter' })(inter_wiki)
-app.route('/email_filter/del/<name>', defaults = { 'tool' : 'del_email_filter' })(inter_wiki_del)
-app.route('/email_filter/add', methods = ['POST', 'GET'], defaults = { 'tool' : 'plus_email_filter' })(inter_wiki_add)
+app.route('/email_filter', defaults = { 'tool' : 'email_filter' })(filter_inter_wiki)
+app.route('/email_filter/del/<name>', defaults = { 'tool' : 'del_email_filter' })(filter_inter_wiki_delete)
+app.route('/email_filter/add', methods = ['POST', 'GET'], defaults = { 'tool' : 'plus_email_filter' })(filter_inter_wiki_add)
 
-app.route('/file_filter', defaults = { 'tool' : 'file_filter' })(inter_wiki)
-app.route('/file_filter/del/<name>', defaults = { 'tool' : 'del_file_filter' })(inter_wiki_del)
-app.route('/file_filter/add', methods = ['POST', 'GET'], defaults = { 'tool' : 'plus_file_filter' })(inter_wiki_add)
+app.route('/file_filter', defaults = { 'tool' : 'file_filter' })(filter_inter_wiki)
+app.route('/file_filter/del/<name>', defaults = { 'tool' : 'del_file_filter' })(filter_inter_wiki_delete)
+app.route('/file_filter/add', methods = ['POST', 'GET'], defaults = { 'tool' : 'plus_file_filter' })(filter_inter_wiki_add)
 
-app.route('/name_filter', defaults = { 'tool' : 'name_filter' })(inter_wiki)
-app.route('/name_filter/del/<name>', defaults = { 'tool' : 'del_name_filter' })(inter_wiki_del)
-app.route('/name_filter/add', methods = ['POST', 'GET'], defaults = { 'tool' : 'plus_name_filter' })(inter_wiki_add)
+app.route('/name_filter', defaults = { 'tool' : 'name_filter' })(filter_inter_wiki)
+app.route('/name_filter/del/<name>', defaults = { 'tool' : 'del_name_filter' })(filter_inter_wiki_delete)
+app.route('/name_filter/add', methods = ['POST', 'GET'], defaults = { 'tool' : 'plus_name_filter' })(filter_inter_wiki_add)
 
-app.route('/extension_filter', defaults = { 'tool' : 'extension_filter' })(inter_wiki)
-app.route('/extension_filter/del/<name>', defaults = { 'tool' : 'del_extension_filter' })(inter_wiki_del)
-app.route('/extension_filter/add', methods = ['POST', 'GET'], defaults = { 'tool' : 'plus_extension_filter' })(inter_wiki_add)
+app.route('/extension_filter', defaults = { 'tool' : 'extension_filter' })(filter_inter_wiki)
+app.route('/extension_filter/del/<name>', defaults = { 'tool' : 'del_extension_filter' })(filter_inter_wiki_delete)
+app.route('/extension_filter/add', methods = ['POST', 'GET'], defaults = { 'tool' : 'plus_extension_filter' })(filter_inter_wiki_add)
 
 # Func-list
 # /list/document/old
-@app.route('/old_page')
-def list_old_page():
-    return list_old_page_2(load_db.db_get())
+app.route('/old_page')(list_old_page)
 
 # /list/document/acl
 @app.route('/acl_list')
@@ -582,11 +491,11 @@ def view_read(name = 'Test', doc_rev = 0, doc_from = ''):
 def edit_revert(name = None):
     return edit_revert_2(load_db.db_get(), name)
 
-@app.route('/edit/<everything:name>', methods = ['POST', 'GET'])
-@app.route('/edit/<everything:name>/doc_section/<int:section>', methods = ['POST', 'GET'])
-def edit(name = 'Test', section = 0):
-    return edit_2(load_db.db_get(), name, section)
+app.route('/edit/<everything:name>', methods = ['POST', 'GET'])(edit)
+app.route('/edit/<everything:name>/doc_from/<everything:name_load>', methods = ['POST', 'GET'])(edit)
+app.route('/edit/<everything:name>/doc_section/<int:section>', methods = ['POST', 'GET'])(edit)
 
+# 개편 예정
 @app.route('/backlink_reset/<everything:name>')
 def edit_backlink_reset(name = 'Test'):
     return edit_backlink_reset_2(load_db.db_get(), name)
@@ -652,13 +561,8 @@ def topic_change(topic_num = 1):
 def topic_admin(topic_num = 1, num = 1):
     return topic_admin_2(load_db.db_get(), topic_num, num)
 
-@app.route('/thread/<int:topic_num>', methods = ['POST', 'GET'])
-def topic(topic_num = 1):
-    return topic_2(load_db.db_get(), topic_num)
-
-@app.route('/topic/<everything:name>', methods = ['POST', 'GET'])
-def topic_close_list(name = 'test'):
-    return topic_close_list_2(load_db.db_get(), name)
+app.route('/thread/<int:topic_num>', methods = ['POST', 'GET'])(topic)
+app.route('/topic/<everything:name>', methods = ['POST', 'GET'])(topic_close_list)
 
 # Func-user
 @app.route('/change', methods = ['POST', 'GET'])
@@ -669,10 +573,15 @@ def user_setting():
 def user_setting_email():
     return user_setting_email_2(load_db.db_get())
 
+app.route('/change/email/delete')(user_setting_email_delete)
+
 @app.route('/change/email/check', methods = ['POST', 'GET'])
 def user_setting_email_check():
     return user_setting_email_check_2(load_db.db_get())
 
+app.route('/change/key')(user_setting_key)
+app.route('/change/key/delete')(user_setting_key_delete)
+
 @app.route('/change/pw', methods = ['POST', 'GET'])
 def user_setting_pw_change():
     return user_setting_pw_change_2(load_db.db_get())
@@ -691,13 +600,8 @@ def user_info(name = ''):
 def user_count_edit(name = None):
     return user_count_edit_2(load_db.db_get(), name)
     
-@app.route('/alarm')
-def user_alarm():
-    return user_alarm_2(load_db.db_get())
-
-@app.route('/alarm/delete')
-def user_alarm_del():
-    return user_alarm_del_2(load_db.db_get())
+app.route('/alarm')(user_alarm)
+app.route('/alarm/delete')(user_alarm_del)
     
 @app.route('/watch_list')
 def user_watch_list():
@@ -746,14 +650,10 @@ def login_register_email_check():
 def login_register_submit():
     return login_register_submit_2(load_db.db_get())
 
-# 개편 필요
-@app.route('/pass_find', methods = ['POST', 'GET'])
-def login_pass_find():
-    return login_pass_find_2(load_db.db_get(), 'pass_find')
-
-@app.route('/pass_find/email', methods = ['POST', 'GET'])
-def login_pass_find_email():
-    return login_pass_find_email_2(load_db.db_get(), 'check_key')
+app.route('/login/find')(login_find)
+app.route('/login/find/key', methods = ['POST', 'GET'])(login_find_key)
+app.route('/login/find/email', methods = ['POST', 'GET'], defaults = { 'tool' : 'pass_find' })(login_find_email)
+app.route('/login/find/email/check', methods = ['POST', 'GET'], defaults = { 'tool' : 'check_key' })(login_find_email_check)
 
 @app.route('/logout')
 def login_logout():
@@ -792,23 +692,30 @@ app.route('/api/w/<everything:name>/doc_tool/<tool>/doc_rev/<int(signed = True):
 app.route('/api/w/<everything:name>/doc_tool/<tool>', methods = ['GET', 'POST'])(api_w)
 app.route('/api/w/<everything:name>', methods = ['GET', 'POST'])(api_w)
 app.route('/api/raw/<everything:name>')(api_raw)
+
 app.route('/api/version', defaults = { 'version_list' : version_list })(api_version)
 app.route('/api/skin_info')(api_skin_info)
 app.route('/api/skin_info/<name>')(api_skin_info)
 app.route('/api/markup')(api_markup)
 app.route('/api/user_info/<name>')(api_user_info)
+app.route('/api/setting/<name>')(api_setting)
+
 app.route('/api/thread/<int:topic_num>/<tool>/<int:num>')(api_topic_sub)
 app.route('/api/thread/<int:topic_num>/<tool>')(api_topic_sub)
 app.route('/api/thread/<int:topic_num>')(api_topic_sub)
+
 app.route('/api/search/<everything:name>/doc_num/<int:num>/<int:page>')(api_search)
 app.route('/api/search/<everything:name>')(api_search)
+
 app.route('/api/recent_change/<int:num>')(api_recent_change)
 app.route('/api/recent_change')(api_recent_change)
 # recent_changes -> recent_change
 app.route('/api/recent_changes')(api_recent_change)
+
 app.route('/api/recent_discuss/<get_type>/<int:num>')(api_recent_discuss)
 app.route('/api/recent_discuss/<int:num>')(api_recent_discuss)
 app.route('/api/recent_discuss')(api_recent_discuss)
+
 app.route('/api/sha224/<everything:data>', methods = ['POST', 'GET'])(api_sha224)
 app.route('/api/title_index')(api_title_index)
 app.route('/api/image/<everything:name>', methods = ['POST', 'GET'])(api_image_view)
@@ -820,8 +727,7 @@ app.route('/api/sitemap.xml')(api_sitemap)
 app.route('/other')(main_tool_other)
 app.route('/manager', methods = ['POST', 'GET'])(main_tool_admin)
 app.route('/manager/<int:num>', methods = ['POST', 'GET'])(main_tool_admin)
-app.route('/manager/<int:num>/<add_1>', methods = ['POST', 'GET'])(main_tool_admin)
-app.route('/manager/<int:num>/<add_1>/<add_2>', methods = ['POST', 'GET'])(main_tool_admin)
+app.route('/manager/<int:num>/<add_2>', methods = ['POST', 'GET'])(main_tool_admin)
 
 app.route('/random')(main_func_random)
 app.route('/upload', methods = ['POST', 'GET'])(main_func_upload)
@@ -843,10 +749,11 @@ app.route('/restart', methods = ['POST', 'GET'])(main_sys_restart)
 app.route('/update', methods = ['POST', 'GET'])(main_sys_update)
 
 app.errorhandler(404)(main_error_404)
-    
+
 if __name__ == "__main__":
-    do_server = netius.servers.WSGIServer(app = app)
-    do_server.serve(
+    waitress.serve(
+        app,
         host = server_set['host'],
-        port = int(server_set['port'])
+        port = int(server_set['port']),
+        threads = 1
     )

+ 2 - 98
emergency_tool.py

@@ -8,106 +8,10 @@ while True:
         break
 
 if data_db_load == 'Y':
-    # Init-DB
-    if os.path.exists(os.path.join('data', 'set.json')):
-        db_set_list = ['db', 'db_type']
-        set_data = json.loads(open(
-            os.path.join('data', 'set.json'),
-            encoding='utf8'
-        ).read())
-        for i in db_set_list:
-            if i not in set_data:
-                print('Please delete set.json')
-                print('----')
-                raise KeyError(i)
-
-        print('DB name : ' + set_data['db'])
-        print('DB type : ' + set_data['db_type'])
-    elif os.getenv('NAMU_DB') or os.getenv('NAMU_DB_TYPE'):
-        set_data = {}
-
-        if os.getenv('NAMU_DB'):
-            set_data['db'] = os.getenv('NAMU_DB')
-        else:
-            set_data['db'] = 'data'
-
-        if os.getenv('NAMU_DB_TYPE'):
-            set_data['db'] = os.getenv('NAMU_DB_TYPE')
-        else:
-            set_data['db'] = 'sqlite'
-
-        print('DB name : ' + set_data['db'])
-        print('DB type : ' + set_data['db_type'])
-    else:
-        set_data = {}
-        normal_db_type = ['sqlite', 'mysql']
-
-        data_get = input('DB type (' + normal_db_type[0] + ') [' + ', '.join(normal_db_type) + '] : ')
-        if data_get == '' or not data_get in normal_db_type:
-            set_data['db_type'] = 'sqlite'
-        else:
-            set_data['db_type'] = data_get
-
-        all_src = []
-        if set_data['db_type'] == 'sqlite':
-            for i_data in os.listdir("."):
-                f_src = re.search(r"(.+)\.db$", i_data)
-                if f_src:
-                    all_src += [f_src.group(1)]
-
-        data_get = input('DB name (data) [' + ', '.join(all_src) + '] : ')
-        if data_get == '':
-            set_data['db'] = 'data'
-        else:
-            set_data['db'] = data_get
-
-        with open(os.path.join('data', 'set.json'), 'w', encoding='utf8') as f:
-            f.write(json.dumps(set_data))
-
-    data_db_set = {'name': set_data['db'], 'type': set_data['db_type']}
-
-    if data_db_set['type'] == 'mysql':
-        if os.path.exists(os.path.join('data', 'mysql.json')):
-            with open(os.path.join('data', 'mysql.json'), encoding='utf8') as f:
-                set_data = json.loads(f.read())
-
-            for i in ('user', 'password', 'host', 'port'):
-                if i not in set_data:
-                    print('Please delete mysql.json')
-                    print('----')
-                    raise KeyError(i)
-
-            set_data_mysql = set_data
-        else:
-            set_data_mysql = {
-                'user': input('DB user ID : '),
-                'password': input('DB password : '),
-                'host': input('DB host (localhost) : '),
-                'port': input('DB port (3306) : '),
-            }
-
-            if set_data_mysql['host'] == '':
-                set_data_mysql['host'] = 'localhost'
-
-            if set_data_mysql['port'] == '':
-                set_data_mysql['port'] = '3306'
-
-            with open(os.path.join('data', 'mysql.json'), 'w', encoding='utf8') as f:
-                f.write(json.dumps(set_data_mysql))
-
-        data_db_set['mysql_user'] = set_data_mysql['user']
-        data_db_set['mysql_pw'] = set_data_mysql['password']
-        if 'host' in set_data_mysql:
-            data_db_set['mysql_host'] = set_data_mysql['host']
-        else:
-            data_db_set['mysql_host'] = 'localhost'
-
-        if 'port' in set_data_mysql:
-            data_db_set['mysql_port'] = set_data_mysql['port']
-        else:
-            data_db_set['mysql_port'] = '3306'
+    data_db_set = class_check_json()
 
     db_data_get(data_db_set['type'])
+    do_db_set(data_db_set)
     load_db = get_db_connect_old(data_db_set)
 
     conn = load_db.db_load()

+ 13 - 1
lang/en-US.json

@@ -1,4 +1,5 @@
 {
+    "language_tag": "en-US",
     "_comment_1_" : "Common",
         "server" : "Server",
         "filter" : "Filter",
@@ -104,6 +105,7 @@
         "empty" : "Empty",
         "domain" : "Domain",
         "result" : "Result",
+        "incomplete" : "Incomplete",
         "_comment_1.1_" : "Time",
             "second" : "Second(s)",
             "hour" : "Hour(s)",
@@ -215,6 +217,10 @@
         "record_reset" : "User edit log reset",
         "today_doc" : "Today[s] document",
         "send_edit" : "Modify reason",
+        "password_instead_key" : "Authentication key",
+        "key_change" : "Change key",
+        "key_delete" : "Delete key",
+        "email_delete" : "Delete email",
         "_comment_2.1_" : "Filter",
             "_comment_2.1.1_" : "List",
                 "interwiki_list" : "Interwiki(s) list",
@@ -225,6 +231,7 @@
                 "edit_tool_list" : "Edit tool(s) list",
                 "image_license_list" : "Image license(s) list",
                 "extension_filter_list" : "Extension filter(s) list",
+                "document_filter_list" : "Document filter(s) list",
             "_comment_2.1.2_" : "Add",
                 "interwiki_add" : "Add Interwiki",
                 "edit_filter_add" : "Add contents filter",
@@ -234,6 +241,7 @@
                 "edit_tool_add" : "Add edit tool",
                 "image_license_add" : "Add image license",
                 "extension_filter_add" : "Add extension filter",
+                "document_filter_add" : "Add document filter",
         "_comment_2.2_" : "Setting",
             "setting" : "Setting",
             "restart_required" : "Restart required",
@@ -291,8 +299,11 @@
                 "edit_help" : "Editing textarea phrase",
                 "upload_help" : "File upload phrase",
                 "upload_default" : "File upload other Default",
+                "topic_text" : "Discussion textarea phrase",
             "_comment_2.2.4_" : "Ext_API",
                 "recaptcha" : "reCAPTCHA",
+                "hcaptcha" : "hCAPTCHA",
+                "captcha" : "CAPTCHA",
                 
                 "email_setting" : "Email settings",
                 "_comment_2.2.4.1_" : "SMTP",
@@ -332,7 +343,8 @@
             "_comment_2.3.3_" : "Record",
                 "edit_record" : "Edit record",
                 "discussion_record" : "Discussion record",
-        "_comment_2.4_" : "Topic tool",
+        "_comment_2.4_" : "Topic",
+            "make_new_topic" : "Make new discussion",
             "topic_tool" : "Discussion management tools",
             "topic_state" : "Discussion status",
             "topic_delete" : "Delete this thread",

+ 15 - 3
lang/ko-KR.json

@@ -1,4 +1,5 @@
 {
+    "language_tag": "ko-KR",
     "delete_admin_group": "관리자 그룹 삭제",
     "document_name": "문서명",
     "non_login_alert": "비로그인 알림",
@@ -406,6 +407,17 @@
     "record_reset": "사용자 편집 기록 초기화",
     "email_setting": "이메일 설정",
     "oauth_client_id": "OAuth 클라이언트 ID",
-    "today_doc" : "오늘의 문서",
-    "send_edit" : "편집 사유 수정"
-}
+    "today_doc": "오늘의 문서",
+    "send_edit": "편집 사유 수정",
+    "document_filter_list": "문서명 필터 목록",
+    "document_filter_add": "문서명 필터 추가",
+    "password_instead_key": "대체 인증키",
+    "key_change": "키 변경",
+    "key_delete": "키 삭제",
+    "email_delete": "이메일 삭제",
+    "incomplete": "미완성",
+    "topic_text": "토론 기본 문구",
+    "hcaptcha": "hCAPTCHA",
+    "captcha": "CAPTCHA",
+    "make_new_topic": "새 토론 생성"
+}

+ 3 - 2
readme-en.md

@@ -1,7 +1,8 @@
-[(en-US)](./readme-en.md) | [(ko-KR)](./readme.md)
+[(en-US)](./readme-en.md) | [(ko-KR)](./readme.md)
 
 # openNAMU
-[![Up to Python 3.6](https://img.shields.io/badge/python->=%203.6-blue.svg)](https://python.org)
+[![Up to Python 3.7](https://img.shields.io/badge/python->=%203.7-blue.svg)](https://python.org)
+[![Up to PyPy 3.7](https://img.shields.io/badge/pypy3-%3E=%203.7-blue.svg)](https://www.pypy.org/)
 [![LICENSE](https://img.shields.io/badge/license-BSD%203--Clause-lightgrey.svg)](./LICENSE)
 
 ![](https://raw.githubusercontent.com/openNAMU/openNAMU/beta/.github/logo.png)

+ 2 - 1
readme.md

@@ -1,7 +1,8 @@
 [(en-US)](./readme-en.md) | [(ko-KR)](./readme.md)
 
 # 오픈나무
-[![Python 3.6 이상](https://img.shields.io/badge/python->=%203.6-blue.svg)](https://python.org)
+[![Python 3.7 이상](https://img.shields.io/badge/python->=%203.7-blue.svg)](https://python.org)
+[![PyPy 3.7 이상](https://img.shields.io/badge/pypy3-%3E=%203.7-blue.svg)](https://www.pypy.org/)
 [![라이선스](https://img.shields.io/badge/license-BSD%203--Clause-lightgrey.svg)](./LICENSE)
 
 ![](https://raw.githubusercontent.com/openNAMU/openNAMU/beta/.github/logo.png)

+ 21 - 0
route/api_setting.py

@@ -0,0 +1,21 @@
+from .tool.func import *
+
+def api_setting(name = 'markup'):
+    with get_db_connect() as conn:
+        curs = conn.cursor()
+        
+        ok_list_1 = ['markup']
+        ok_list_2 = ['inter_wiki']
+        
+        if name in ok_list_1:
+            curs.execute(db_change('select data from other where name = ?'), [name])
+            rep_data = curs.fetchall()
+            if rep_data:
+                return flask.jsonify({ name : rep_data })
+        elif name in ok_list_2:
+            curs.execute(db_change("select html, plus, plus_t from html_filter where kind = ?"), [name])
+            rep_data = curs.fetchall()
+            if rep_data:
+                return flask.jsonify({ name : rep_data })
+
+        return flask.jsonify({})

+ 220 - 218
route/edit.py

@@ -1,236 +1,238 @@
 from .tool.func import *
 
-def edit_2(conn, name, section):
-    curs = conn.cursor()
-
-    ip = ip_check()
-    if acl_check(name) == 1:
-        return re_error('/ban')
-    
-    curs.execute(db_change("select id from history where title = ? order by id + 0 desc"), [name])
-    doc_ver = curs.fetchall()
-    doc_ver = doc_ver[0][0] if doc_ver else '0'
-    
-    section = '' if section == 0 else section
-    post_ver = flask.request.form.get('ver', '')
-    if flask.request.method == 'POST':
-        edit_repeat = 'error' if post_ver != doc_ver else 'post'
-    else:
-        edit_repeat = 'get'
+def edit(name = 'Test', name_load = 0, section = 0):
+    with get_db_connect() as conn:
+        curs = conn.cursor()
     
-    if edit_repeat == 'post':
-        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)
-
-        if slow_edit_check() == 1:
-            return re_error('/error/24')
-
-        today = get_time()
-        content = flask.request.form.get('content', '').replace('\r\n', '\n')
+        ip = ip_check()
+        if acl_check(name) == 1:
+            return re_error('/ban')
         
-        if edit_filter_do(content) == 1:
-            return re_error('/error/21')
-            
-        curs.execute(db_change('select data from other where name = "copyright_checkbox_text"'))
-        copyright_checkbox_text_d = curs.fetchall()
-        if copyright_checkbox_text_d and copyright_checkbox_text_d[0][0] != '' and flask.request.form.get('copyright_agreement', '') != 'yes':
-            return re_error('/error/29')
+        curs.execute(db_change("select id from history where title = ? order by id + 0 desc"), [name])
+        doc_ver = curs.fetchall()
+        doc_ver = doc_ver[0][0] if doc_ver else '0'
         
-        curs.execute(db_change("select data from data where title = ?"), [name])
-        old = curs.fetchall()
-        if old:  
-            o_data = old[0][0].replace('\r\n', '\n')
-
-            leng = leng_check(len(o_data), len(content))
-            
-            curs.execute(db_change("update data set data = ? where title = ?"), [content, name])
+        section = '' if section == 0 else section
+        post_ver = flask.request.form.get('ver', '')
+        if flask.request.method == 'POST':
+            edit_repeat = 'error' if post_ver != doc_ver else 'post'
         else:
-            leng = '+' + str(len(content))
-
-            curs.execute(db_change("insert into data (title, data) values (?, ?)"), [name, content])
-
-            curs.execute(db_change('select data from other where name = "count_all_title"'))
-            curs.execute(db_change("update other set data = ? where name = 'count_all_title'"), [str(int(curs.fetchall()[0][0]) + 1)])
-
-        curs.execute(db_change("select user from scan where title = ? and type = ''"), [name])
-        for scan_user in curs.fetchall():
-            add_alarm(scan_user[0], ip + ' | <a href="/w/' + url_pas(name) + '">' + name + '</a> | Edit')
-                
-        history_plus(
-            name,
-            content,
-            today,
-            ip,
-            flask.request.form.get('send', ''),
-            leng
-        )
-        
-        curs.execute(db_change("delete from back where link = ?"), [name])
-        curs.execute(db_change("delete from back where title = ? and type = 'no'"), [name])
-        
-        render_set(
-            doc_name = name,
-            doc_data = content,
-            data_type = 'backlink'
-        )
-        
-        conn.commit()
-        
-        section = (('#edit_load_' + str(section)) if section != '' else '')
+            edit_repeat = 'get'
         
-        return redirect('/w/' + url_pas(name) + section)
-    else:
-        editor_top_text = ''
-        if edit_repeat == 'get':
-            load_title = flask.request.args.get('plus', '')
-            if load_title == '':
-                load_title = name
-                editor_top_text += '<a href="/manager/15?plus=' + url_pas(name) + '">(' + load_lang('load') + ')</a> '
+        if edit_repeat == 'post':
+            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)
+    
+            if slow_edit_check() == 1:
+                return re_error('/error/24')
+    
+            today = get_time()
+            content = flask.request.form.get('content', '').replace('\r\n', '\n')
+            
+            if edit_filter_do(content) == 1:
+                return re_error('/error/21')
                 
-            curs.execute(db_change("select data from data where title = ?"), [load_title])
-            sql_d = curs.fetchall()
-            data = sql_d[0][0] if sql_d else ''
-            data = data.replace('\r\n', '\n')
+            curs.execute(db_change('select data from other where name = "copyright_checkbox_text"'))
+            copyright_checkbox_text_d = curs.fetchall()
+            if copyright_checkbox_text_d and copyright_checkbox_text_d[0][0] != '' and flask.request.form.get('copyright_agreement', '') != 'yes':
+                return re_error('/error/29')
+            
+            curs.execute(db_change("select data from data where title = ?"), [name])
+            old = curs.fetchall()
+            if old:  
+                o_data = old[0][0].replace('\r\n', '\n')
+    
+                leng = leng_check(len(o_data), len(content))
+                
+                curs.execute(db_change("update data set data = ? where title = ?"), [content, name])
+            else:
+                leng = '+' + str(len(content))
+    
+                curs.execute(db_change("insert into data (title, data) values (?, ?)"), [name, content])
+    
+                curs.execute(db_change('select data from other where name = "count_all_title"'))
+                curs.execute(db_change("update other set data = ? where name = 'count_all_title'"), [str(int(curs.fetchall()[0][0]) + 1)])
+    
+            curs.execute(db_change("select user from scan where title = ? and type = ''"), [name])
+            for scan_user in curs.fetchall():
+                add_alarm(scan_user[0], ip + ' | <a href="/w/' + url_pas(name) + '">' + name + '</a> | Edit')
                     
+            history_plus(
+                name,
+                content,
+                today,
+                ip,
+                flask.request.form.get('send', ''),
+                leng
+            )
+            
+            curs.execute(db_change("delete from back where link = ?"), [name])
+            curs.execute(db_change("delete from back where title = ? and type = 'no'"), [name])
+            
+            render_set(
+                doc_name = name,
+                doc_data = content,
+                data_type = 'backlink'
+            )
+            
+            conn.commit()
+            
+            section = (('#edit_load_' + str(section)) if section != '' else '')
+            
+            return redirect('/w/' + url_pas(name) + section)
         else:
-            data = flask.request.form.get('content', '')
-            warning_edit = load_lang('exp_edit_conflict') + ' '
-
-            if flask.request.form.get('ver', '0') == '0':
-                warning_edit += '<a href="/raw/' + url_pas(name) + '">(r' + doc_ver + ')</a>'
+            editor_top_text = ''
+            if edit_repeat == 'get':
+                load_title = name_load
+                if load_title == 0 and section == '':
+                    load_title = name
+                    editor_top_text += '<a href="/manager/15/' + url_pas(name) + '">(' + load_lang('load') + ')</a> '
+                elif section != '':
+                    load_title = name
+                    
+                curs.execute(db_change("select data from data where title = ?"), [load_title])
+                sql_d = curs.fetchall()
+                data = sql_d[0][0] if sql_d else ''
+                data = data.replace('\r\n', '\n')
             else:
-                warning_edit += '' + \
-                    '<a href="/diff/' + flask.request.form.get('ver', '1') + '/' + doc_ver + '/' + url_pas(name) + '">' + \
-                        '(r' + doc_ver + ')' + \
-                    '</a>' + \
-                ''
-
-            warning_edit += '<hr class="main_hr">'
-            editor_top_text = warning_edit + editor_top_text
-
-        editor_top_text += '' + \
-            '<a href="/edit_filter">(' + load_lang('edit_filter_rule') + ')</a>' + \
-            '<hr class="main_hr">' + \
-        ''
-
-        curs.execute(db_change('select data from other where name = "edit_bottom_text"'))
-        sql_d = curs.fetchall()
-        b_text = ('<hr class="main_hr">' + sql_d[0][0]) if sql_d and sql_d[0][0] != '' else ''
-        
-        curs.execute(db_change('select data from other where name = "copyright_checkbox_text"'))
-        sql_d = curs.fetchall()
-        if sql_d and sql_d[0][0] != '':
-            cccb_text = '' + \
-                '<hr class="main_hr">' + \
-                '<input type="checkbox" name="copyright_agreement" value="yes"> ' + sql_d[0][0] + \
+                data = flask.request.form.get('content', '')
+                warning_edit = load_lang('exp_edit_conflict') + ' '
+    
+                if flask.request.form.get('ver', '0') == '0':
+                    warning_edit += '<a href="/raw/' + url_pas(name) + '">(r' + doc_ver + ')</a>'
+                else:
+                    warning_edit += '' + \
+                        '<a href="/diff/' + flask.request.form.get('ver', '1') + '/' + doc_ver + '/' + url_pas(name) + '">' + \
+                            '(r' + doc_ver + ')' + \
+                        '</a>' + \
+                    ''
+    
+                warning_edit += '<hr class="main_hr">'
+                editor_top_text = warning_edit + editor_top_text
+    
+            editor_top_text += '' + \
+                '<a href="/edit_filter">(' + load_lang('edit_filter_rule') + ')</a>' + \
                 '<hr class="main_hr">' + \
             ''
-        else:
-            cccb_text = ''
-
-        curs.execute(db_change('select data from other where name = "edit_help"'))
-        sql_d = curs.fetchall()
-        p_text = html.escape(sql_d[0][0]) if sql_d and sql_d[0][0] != '' else load_lang('default_edit_help')
-
-        data = re.sub(r'\n+$', '', data)
-
-        monaco_on = flask.request.cookies.get('main_css_monaco', '0')
-        if monaco_on == '1':
-            editor_display = 'style="display: none;"'
-            monaco_display = ''
-            add_get_file = '''
-                <link   rel="stylesheet"
-                        data-name="vs/editor/editor.main" 
-                        href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs/editor/editor.main.min.css">
-                <script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs/loader.min.js"></script>
-            '''
+    
+            curs.execute(db_change('select data from other where name = "edit_bottom_text"'))
+            sql_d = curs.fetchall()
+            b_text = ('<hr class="main_hr">' + sql_d[0][0]) if sql_d and sql_d[0][0] != '' else ''
             
-            if flask.request.cookies.get('main_css_darkmode', '0') == '1':
-                monaco_thema = 'vs-dark'
+            curs.execute(db_change('select data from other where name = "copyright_checkbox_text"'))
+            sql_d = curs.fetchall()
+            if sql_d and sql_d[0][0] != '':
+                cccb_text = '' + \
+                    '<hr class="main_hr">' + \
+                    '<input type="checkbox" name="copyright_agreement" value="yes"> ' + sql_d[0][0] + \
+                    '<hr class="main_hr">' + \
+                ''
             else:
-                monaco_thema = ''
-            
-            add_script = '''
-                require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs' }});
-                require(["vs/editor/editor.main"], function () {
-                    window.editor = monaco.editor.create(document.getElementById('monaco_editor'), {
-                        value: document.getElementById('textarea_edit_view').value,
-                        language: 'plaintext',
-                        theme: \'''' + monaco_thema + '''\'
+                cccb_text = ''
+    
+            curs.execute(db_change('select data from other where name = "edit_help"'))
+            sql_d = curs.fetchall()
+            p_text = html.escape(sql_d[0][0]) if sql_d and sql_d[0][0] != '' else load_lang('default_edit_help')
+    
+            data = re.sub(r'\n+$', '', data)
+    
+            monaco_on = flask.request.cookies.get('main_css_monaco', '0')
+            if monaco_on == '1':
+                editor_display = 'style="display: none;"'
+                monaco_display = ''
+                add_get_file = '''
+                    <link   rel="stylesheet"
+                            data-name="vs/editor/editor.main" 
+                            href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs/editor/editor.main.min.css">
+                    <script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs/loader.min.js"></script>
+                '''
+                
+                if flask.request.cookies.get('main_css_darkmode', '0') == '1':
+                    monaco_thema = 'vs-dark'
+                else:
+                    monaco_thema = ''
+                
+                add_script = '''
+                    require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.20.0/min/vs' }});
+                    require(["vs/editor/editor.main"], function () {
+                        window.editor = monaco.editor.create(document.getElementById('monaco_editor'), {
+                            value: document.getElementById('textarea_edit_view').value,
+                            language: 'plaintext',
+                            theme: \'''' + monaco_thema + '''\'
+                        });
                     });
-                });
-            '''
-        else:
-            editor_display = ''
-            monaco_display = 'style="display: none;"'
-            add_get_file = ''
-            add_script = ''
+                '''
+            else:
+                editor_display = ''
+                monaco_display = 'style="display: none;"'
+                add_get_file = ''
+                add_script = ''
+                
+            curs.execute(db_change("select data from other where name = 'markup'"))
+            markup = curs.fetchall()[0][0]
             
-        curs.execute(db_change("select data from other where name = 'markup'"))
-        markup = curs.fetchall()[0][0]
-        
-        server_set = {
-            'section' : section,
-            'markup' : markup
-         }
-
-        return easy_minify(flask.render_template(skin_check(), 
-            imp = [name, wiki_set(), wiki_custom(), wiki_css(['(' + load_lang('edit') + ')', 0])],
-            data =  editor_top_text + add_get_file + '''
-                <span   id="server_set"
-                        style="display: none;">''' + json.dumps(server_set) + '''</span>
-                <form method="post">
-                    <div>''' + edit_button(monaco_on) + '''</div>
-                    <div    id="monaco_editor"
-                            class="content" 
-                            ''' + monaco_display + '''></div>
-                    <textarea   id="textarea_edit_view"
-                                ''' + editor_display + '''
-                                class="content"
-                                placeholder="''' + p_text + '''">''' + html.escape(data) + '''</textarea>
-                    <hr class="main_hr">
-                    <input  placeholder="''' + load_lang('why') + '''" 
-                            name="send">
-                    <textarea   style="display: none;" 
-                                id="origin">''' + html.escape(data) + '''</textarea>
-                    <textarea   style="display: none;"
-                                name="content"
-                                id="content"></textarea>
-                    <input  style="display: none;" 
-                            name="ver" 
-                            value="''' + doc_ver + '''">
+            server_set = {
+                'section' : section,
+                'markup' : markup
+             }
+    
+            return easy_minify(flask.render_template(skin_check(), 
+                imp = [name, wiki_set(), wiki_custom(), wiki_css(['(' + load_lang('edit') + ')', 0])],
+                data =  editor_top_text + add_get_file + '''
+                    <span   id="server_set"
+                            style="display: none;">''' + json.dumps(server_set) + '''</span>
+                    <form method="post">
+                        <div>''' + edit_button(monaco_on) + '''</div>
+                        <div    id="monaco_editor"
+                                class="content" 
+                                ''' + monaco_display + '''></div>
+                        <textarea   id="textarea_edit_view"
+                                    ''' + editor_display + '''
+                                    class="content"
+                                    placeholder="''' + p_text + '''">''' + html.escape(data) + '''</textarea>
+                        <hr class="main_hr">
+                        <input  placeholder="''' + load_lang('why') + '''" 
+                                name="send">
+                        <textarea   style="display: none;" 
+                                    id="origin">''' + html.escape(data) + '''</textarea>
+                        <textarea   style="display: none;"
+                                    name="content"
+                                    id="content"></textarea>
+                        <input  style="display: none;" 
+                                name="ver" 
+                                value="''' + doc_ver + '''">
+                        <hr class="main_hr">
+                        ''' + captcha_get() + ip_warning() + cccb_text + '''
+                        <button id="save"
+                                type="submit" 
+                                onclick="
+                                    monaco_to_content(); 
+                                    save_stop_exit();
+                                    section_edit_do();
+                                ">''' + load_lang('save') + '''</button>
+                        <button id="preview" 
+                                type="button" 
+                                onclick="
+                                    monaco_to_content();
+                                    load_preview(\'''' + url_pas(name) + '''\');
+                                ">''' + load_lang('preview') + '''</button>
+                    </form>
+                    ''' + b_text + '''
                     <hr class="main_hr">
-                    ''' + captcha_get() + ip_warning() + cccb_text + '''
-                    <button id="save"
-                            type="submit" 
-                            onclick="
-                                monaco_to_content(); 
-                                save_stop_exit();
-                                section_edit_do();
-                            ">''' + load_lang('save') + '''</button>
-                    <button id="preview" 
-                            type="button" 
-                            onclick="
-                                monaco_to_content();
-                                load_preview(\'''' + url_pas(name) + '''\');
-                            ">''' + load_lang('preview') + '''</button>
-                </form>
-                ''' + b_text + '''
-                <hr class="main_hr">
-                <div id="see_preview"></div>
-                <script>
-                    section_edit_init();
-                    do_paste_image();
-                    do_not_out();
-                    ''' + add_script + '''
-                </script>
-            ''',
-            menu = [
-                ['w/' + url_pas(name), load_lang('return')],
-                ['delete/' + url_pas(name), load_lang('delete')], 
-                ['move/' + url_pas(name), load_lang('move')], 
-                ['upload', load_lang('upload')]
-            ]
-        ))
+                    <div id="see_preview"></div>
+                    <script>
+                        section_edit_init();
+                        do_paste_image();
+                        do_not_out();
+                        ''' + add_script + '''
+                    </script>
+                ''',
+                menu = [
+                    ['w/' + url_pas(name), load_lang('return')],
+                    ['delete/' + url_pas(name), load_lang('delete')], 
+                    ['move/' + url_pas(name), load_lang('move')], 
+                    ['upload', load_lang('upload')]
+                ]
+            ))

+ 41 - 0
route/filter_document.py

@@ -0,0 +1,41 @@
+from .tool.func import *
+
+def filter_document():
+    with get_db_connect() as conn:
+        curs = conn.cursor()
+
+        div = '<table id="main_table_set">'
+        div += '<tr id="main_table_top_tr">'
+        div += '<td id="main_table_width">A</td>'
+        div += '<td id="main_table_width">B</td>'
+        div += '<td id="main_table_width">C</td>'
+        div += '</tr>'
+
+        admin = admin_check()
+        title = load_lang('document_filter_list')
+        
+        curs.execute(db_change("select html, plus, plus_t from html_filter where kind = 'document'"))
+        db_data = curs.fetchall()
+        for data in db_data:
+            div += '<tr>'
+            div += '<td>' + html.escape(data[0])
+            if admin == 1:
+                div += ' <a href="/filter/document/add/' + url_pas(data[0]) + '">(' + load_lang('edit') + ')</a>'
+                div += ' <a href="/filter/document/del/' + url_pas(data[0]) + '">(' + load_lang('delete') + ')</a>'
+
+            div += '</td>'
+            div += '<td>' + html.escape(data[1]) + '</td>'
+            div += '<td>' + html.escape(data[2]) + '</td>'
+            div += '</tr>'
+
+        div += '</table>'
+
+        if admin == 1:
+            div += '<hr class="main_hr">'
+            div += '<a href="/filter/document/add">(' + load_lang('add') + ')</a>'
+
+        return easy_minify(flask.render_template(skin_check(),
+            imp = [title, wiki_set(), wiki_custom(), wiki_css([0, 0])],
+            data = div,
+            menu = [['manager/1', load_lang('return')]]
+        ))

+ 62 - 0
route/filter_document_add.py

@@ -0,0 +1,62 @@
+from .tool.func import *
+
+def filter_document_add(name = ''):
+    with get_db_connect() as conn:
+        curs = conn.cursor()
+
+        if flask.request.method == 'POST':
+            if admin_check(None, 'plus_document_filter') != 1:
+                return re_error('/error/3')
+
+            post_name = flask.request.form.get('name', '')
+            if post_name == '':
+                return redirect('/filter/document/list')
+            
+            post_acl = flask.request.form.get('acl', '')
+            post_regex = flask.request.form.get('regex', '')
+            try:
+                re.compile(post_regex)
+            except:
+                return re_error('/error/23')
+
+            curs.execute(db_change('insert into html_filter (html, kind, plus, plus_t) values (?, ?, ?, ?)'), [
+                post_name,
+                'document',
+                post_regex,
+                post_acl
+            ])
+
+            conn.commit()
+
+            return redirect('/filter/document/list')
+        else:
+            stat = 'disabled' if admin_check() != 1 else ''
+            acl_list = get_acl_list()
+            
+            curs.execute(db_change("select plus, plus_t from html_filter where html = ? and kind = 'document'"), [name])
+            db_data = curs.fetchall()
+            acl_list = [['selected' if db_data and db_data[0][1] == i else '', i] for i in acl_list]
+
+            return easy_minify(flask.render_template(skin_check(),
+                imp = [load_lang('document_filter_add'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
+                data =  '''
+                        <form method="post">
+                            ''' + load_lang('name') + '''
+                            <hr class="main_hr">
+                            <input value="''' + html.escape(name) + '''" type="text" name="name">
+                            <hr class="main_hr">
+                            ''' + load_lang('regex') + '''
+                            <hr class="main_hr">
+                            <input value="''' + (html.escape(db_data[0][0]) if db_data else '') + '''" type="text" name="regex">
+                            <hr class="main_hr">
+                            <a href="/acl/Test#exp">''' + load_lang('acl') + '''</a>
+                            <hr class="main_hr">
+                            <select name="acl">
+                                ''' + ''.join(['<option ' + i[0] + ' value=' + i[1] + '>' + ('normal' if i[1] == '' else i[1]) + '</option>' for i in acl_list]) + '''
+                            </select>
+                            <hr class="main_hr">
+                            <button ''' + stat + ''' type="submit">''' + load_lang('add') + '''</button>
+                        </form>
+                        ''',
+                menu = [['filter/document/list', load_lang('return')]]
+            ))

+ 13 - 0
route/filter_document_delete.py

@@ -0,0 +1,13 @@
+from .tool.func import *
+
+def filter_document_delete(name = ''):
+    with get_db_connect() as conn:
+        curs = conn.cursor()
+
+        if admin_check(None, 'del_document_filter') != 1:
+            return re_error('/error/3')
+
+        curs.execute(db_change("delete from html_filter where html = ? and kind = 'document'"), [name])
+        conn.commit()
+
+        return redirect('/filter/document/list')

+ 11 - 9
route/inter_wiki.py → route/filter_inter_wiki.py

@@ -1,6 +1,6 @@
 from .tool.func import *
 
-def inter_wiki(tool):
+def filter_inter_wiki(tool):
     with get_db_connect() as conn:
         curs = conn.cursor()
 
@@ -34,10 +34,6 @@ def inter_wiki(tool):
         elif tool == 'file_filter':
             title = load_lang('file_filter_list')
 
-            curs.execute(db_change("select html, plus, plus_t from html_filter where kind = 'file'"))
-        elif tool == 'file_filter':
-            title = load_lang('file_filter_list')
-
             curs.execute(db_change("select html, plus, plus_t from html_filter where kind = 'file'"))
         elif tool == 'image_license':
             title = load_lang('image_license_list')
@@ -57,19 +53,25 @@ def inter_wiki(tool):
             div += '<tr>'
             div += '<td>'
 
-            div += data[0]
+            div += html.escape(data[0])
             if admin == 1:
-                div += ' <a href="/' + tool + '/add/' + url_pas(data[0]) + '">(' + load_lang('edit') + ')</a>'
+                if tool in ('inter_wiki', 'edit_filter'):
+                    div += ' <a href="/' + tool + '/add/' + url_pas(data[0]) + '">(' + load_lang('edit') + ')</a>'
+                    
                 div += ' <a href="/' + tool + '/del/' + url_pas(data[0]) + '">(' + load_lang('delete') + ')</a>'
 
             div += '</td>'
 
             if tool == 'inter_wiki':
-                div += '<td><a id="out_link" href="' + data[1] + '">' + html.escape(data[1]) + '</a></td>'
+                div += '<td><a id="out_link" href="' + html.escape(data[1]) + '">' + html.escape(data[1]) + '</a></td>'
             else:
                 div += '<td>' + html.escape(data[1]) + '</td>'
 
-            div += '<td>' + html.escape(data[2]) + '</td>'
+            if tool == 'inter_wiki':
+                div += '<td>' + data[2] + '</td>'
+            else:
+                div += '<td>' + html.escape(data[2]) + '</td>'
+            
             div += '</tr>'
 
         div += '</table>'

+ 12 - 12
route/inter_wiki_add.py → route/filter_inter_wiki_add.py

@@ -1,14 +1,16 @@
 from .tool.func import *
 
-def inter_wiki_add(tool, name = None):
+def filter_inter_wiki_add(tool, name = None):
     with get_db_connect() as conn:
         curs = conn.cursor()
 
-        if not name:
-            if tool == 'plus_edit_filter':
-                return redirect('/manager/9')
+        if not name and tool == 'plus_edit_filter':
+            return redirect('/manager/9')
 
         if flask.request.method == 'POST':
+            if admin_check() != 1:
+                return re_error('/error/3')
+
             if tool == 'plus_inter_wiki':
                 if name:
                     curs.execute(db_change("delete from html_filter where html = ? and kind = 'inter_wiki'"), [name])
@@ -88,10 +90,11 @@ def inter_wiki_add(tool, name = None):
                         type_d
                     ])
 
-                curs.execute(db_change('insert into html_filter (html, kind, plus) values (?, ?, ?)'), [
+                curs.execute(db_change('insert into html_filter (html, kind, plus, plus_t) values (?, ?, ?, ?)'), [
                     flask.request.form.get('title', 'test'),
                     type_d,
-                    plus_d
+                    plus_d,
+                    ''
                 ])
 
             conn.commit()
@@ -99,10 +102,7 @@ def inter_wiki_add(tool, name = None):
             return redirect('/' + re.sub(r'^plus_', '', tool))
         else:
             get_sub = 0
-            if admin_check() != 1:
-                stat = 'disabled'
-            else:
-                stat = ''
+            stat = 'disabled' if admin_check() != 1 else ''
 
             if tool == 'plus_inter_wiki':
                 if name:
@@ -163,9 +163,9 @@ def inter_wiki_add(tool, name = None):
                 form_data = '''
                     <script>function insert_v(name, data) { document.getElementById(name).value = data; }</script>''' + insert_data + '''
                     <hr class="main_hr">
-                    <input ''' + stat + ''' placeholder="''' + load_lang('second') + '''" id="second" name="second" type="text" value="''' + html.escape(time_data) + '''">
+                    <input placeholder="''' + load_lang('second') + '''" id="second" name="second" type="text" value="''' + html.escape(time_data) + '''">
                     <hr class="main_hr">
-                    <input ''' + stat + ''' placeholder="''' + load_lang('regex') + '''" name="content" value="''' + html.escape(textarea) + '''" type="text">
+                    <input placeholder="''' + load_lang('regex') + '''" name="content" value="''' + html.escape(textarea) + '''" type="text">
                 '''
             elif tool == 'plus_name_filter':
                 title = load_lang('id_filter_add')

+ 29 - 0
route/filter_inter_wiki_delete.py

@@ -0,0 +1,29 @@
+from .tool.func import *
+
+def filter_inter_wiki_delete(tool, name = 'Test'):
+    with get_db_connect() as conn:
+        curs = conn.cursor()
+        
+        if admin_check(None, tool) != 1:
+            return re_error('/error/3')
+
+        if tool == 'del_inter_wiki':
+            curs.execute(db_change("delete from html_filter where html = ? and kind = 'inter_wiki'"), [name])
+        elif tool == 'del_edit_filter':
+            curs.execute(db_change("delete from html_filter where html = ? and kind = 'regex_filter'"), [name])
+        elif tool == 'del_name_filter':
+            curs.execute(db_change("delete from html_filter where html = ? and kind = 'name'"), [name])
+        elif tool == 'del_file_filter':
+            curs.execute(db_change("delete from html_filter where html = ? and kind = 'file'"), [name])
+        elif tool == 'del_email_filter':
+            curs.execute(db_change("delete from html_filter where html = ? and kind = 'email'"), [name])
+        elif tool == 'del_image_license':
+            curs.execute(db_change("delete from html_filter where html = ? and kind = 'image_license'"), [name])
+        elif tool == '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])
+
+        conn.commit()
+
+        return redirect('/' + re.sub(r'^del_', '', tool))

+ 2 - 2
route/give_user_check.py

@@ -29,7 +29,7 @@ def give_user_check_2(conn, name):
                     div += '''
                         <table id="main_table_set">
                             <tbody>
-                                <tr>
+                                <tr id="main_table_top_tr">
                                     <td>Q</td>
                                     <td>''' + approval_question[0][0] + '''</td>
                                     <td>A</td>
@@ -94,7 +94,7 @@ def give_user_check_2(conn, name):
             div += '''
                 <table id="main_table_set">
                     <tbody>
-                        <tr>
+                        <tr id="main_table_top_tr">
                             <td id="main_table_width">''' + load_lang('name') + '''</td>
                             <td id="main_table_width">''' + load_lang('ip') + '''</td>
                             <td id="main_table_width">''' + load_lang('time') + '''</td>

+ 0 - 29
route/inter_wiki_del.py

@@ -1,29 +0,0 @@
-from .tool.func import *
-
-def inter_wiki_del(tool, name = 'Test'):
-    with get_db_connect() as conn:
-        curs = conn.cursor()
-
-        if admin_check(None, tool) == 1:
-            if tool == 'del_inter_wiki':
-                curs.execute(db_change("delete from html_filter where html = ? and kind = 'inter_wiki'"), [name])
-            elif tool == 'del_edit_filter':
-                curs.execute(db_change("delete from html_filter where html = ? and kind = 'regex_filter'"), [name])
-            elif tool == 'del_name_filter':
-                curs.execute(db_change("delete from html_filter where html = ? and kind = 'name'"), [name])
-            elif tool == 'del_file_filter':
-                curs.execute(db_change("delete from html_filter where html = ? and kind = 'file'"), [name])
-            elif tool == 'del_email_filter':
-                curs.execute(db_change("delete from html_filter where html = ? and kind = 'email'"), [name])
-            elif tool == 'del_image_license':
-                curs.execute(db_change("delete from html_filter where html = ? and kind = 'image_license'"), [name])
-            elif tool == '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])
-
-            conn.commit()
-
-            return redirect('/' + re.sub(r'^del_', '', tool))
-        else:
-            return re_error('/error/3')

+ 28 - 35
route/list_old_page.py

@@ -1,37 +1,30 @@
 from .tool.func import *
 
-def list_old_page_2(conn):
-    curs = conn.cursor()
-
-    num = int(number_check(flask.request.args.get('num', '1')))
-    if num * 50 > 0:
-        sql_num = num * 50 - 50
-    else:
-        sql_num = 0
-
-    curs.execute(db_change('select data from other where name = "count_all_title"'))
-    if int(curs.fetchall()[0][0]) > 30000:
-        return re_error('/error/25')
-
-    div = '<ul class="inside_ul">'
-
-    curs.execute(db_change('' + \
-        'select title, date from history h ' + \
-        "where title not like 'user:%' and title not like 'category:%' and title not like 'file:%' and " + \
-        "exists (select title from data where title = h.title) and " + \
-        "not exists (select title from back where link = h.title and type = 'redirect') " + \
-        'group by title ' + \
-        'order by date asc ' + \
-        'limit ?, 50' + \
-    ''), [sql_num])
-    n_list = curs.fetchall()
-    for data in n_list:
-        div += '<li><a href="/w/' + url_pas(data[0]) + '">' + html.escape(data[0]) + '</a> (' + re.sub(r' .*$', '', data[1]) + ')</li>'
-
-    div += '</ul>' + next_fix('/old_page?num=', num, n_list)
-
-    return easy_minify(flask.render_template(skin_check(),
-        imp = [load_lang('old_page'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
-        data = div,
-        menu = [['other', load_lang('return')]]
-    ))
+def list_old_page():
+    with get_db_connect() as conn:
+        curs = conn.cursor()
+        
+        num = flask.request.args.get('num', '1')
+        num = int(number_check(num))
+        sql_num = (num * 50 - 50) if num * 50 > 0 else 0
+        
+        curs.execute(db_change('select data from other where name = "count_all_title"'))
+        if int(curs.fetchall()[0][0]) > 30000:
+            return re_error('/error/25')
+        
+        div = '<ul class="inside_ul">'
+        
+        curs.execute(db_change('' + \
+            'select h.title, max(h.date) from history as h where not (title like "user:%" or title like "category:%" or title like "file:%") and exists (select title from data where title = h.title) and not exists (select title from back where link = h.title and type = "redirect") group by h.title order by h.date asc limit ?, 50' + \
+        ''), [sql_num])
+        n_list = curs.fetchall()
+        for data in n_list:
+            div += '<li>' + data[1] + ' | <a href="/w/' + url_pas(data[0]) + '">' + html.escape(data[0]) + '</a></li>'
+        
+        div += '</ul>' + next_fix('/old_page?num=', num, n_list)
+        
+        return easy_minify(flask.render_template(skin_check(),
+            imp = [load_lang('old_page'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
+            data = div,
+            menu = [['other', load_lang('return')]]
+        ))

+ 13 - 0
route/login_find.py

@@ -0,0 +1,13 @@
+from .tool.func import *
+
+def login_find():
+    return easy_minify(flask.render_template(skin_check(),
+        imp = [load_lang('password_search'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
+        data = '''
+            <ul class="inside_ul">
+                <li><a href="/login/find/email">''' + load_lang('email') + '''</a></li>
+                <li><a href="/login/find/key">''' + load_lang('key') + '''</a></li>
+            </ul>
+        ''',
+        menu = [['user', load_lang('return')]]
+    ))

+ 116 - 0
route/login_find_email.py

@@ -0,0 +1,116 @@
+from .tool.func import *
+
+# 개편 필요
+def login_find_email(tool):
+    with get_db_connect() as conn:
+        curs = conn.cursor()
+        
+        if flask.request.method == 'POST':
+            re_set_list = ['c_id', 'c_pw', 'c_ans', 'c_que', 'c_key', 'c_type']
+        
+            if tool == 'email_change':
+                flask.session['c_key'] = load_random_key(32)
+                flask.session['c_id'] = ip_check()
+                flask.session['c_type'] = 'email_change'
+            elif tool == 'pass_find':
+                user_id = flask.request.form.get('id', '')
+                user_email = flask.request.form.get('email', '')
+        
+                flask.session['c_key'] = load_random_key(32)
+                flask.session['c_id'] = user_id
+                flask.session['c_type'] = 'pass_find'
+            else:
+                if not 'c_type' in flask.session:
+                    return redirect('/register')
+        
+            if tool != 'pass_find':
+                user_email = flask.request.form.get('email', '')
+                email_data = re.search(r'@([^@]+)$', user_email)
+                if email_data:
+                    curs.execute(db_change("select html from html_filter where html = ? and kind = 'email'"), [email_data.group(1)])
+                    if not curs.fetchall():
+                        for i in re_set_list:
+                            flask.session.pop(i, None)
+                        
+                        return redirect('/email_filter')
+                else:
+                    for i in re_set_list:
+                        flask.session.pop(i, None)
+                    
+                    return re_error('/error/36')
+        
+            curs.execute(db_change('select data from other where name = "email_title"'))
+            sql_d = curs.fetchall()
+            t_text = html.escape(sql_d[0][0]) if sql_d and sql_d[0][0] != '' else (wiki_set()[0] + ' key')
+        
+            curs.execute(db_change('select data from other where name = "email_text"'))
+            sql_d = curs.fetchall()
+            i_text = (html.escape(sql_d[0][0]) + '\n\nKey : ' + flask.session['c_key']) if sql_d and sql_d[0][0] != '' else ('Key : ' + flask.session['c_key'])
+            
+            if tool == 'pass_find':
+                curs.execute(db_change("select id from user_set where id = ? and name = 'email' and data = ?"), [user_id, user_email])
+                if not curs.fetchall():
+                    return re_error('/error/12')
+                    
+                if send_email(user_email, t_text, i_text) == 0:
+                    return re_error('/error/18')
+        
+                return redirect('/pass_find/email')
+            else:
+                curs.execute(db_change('select id from user_set where name = "email" and data = ?'), [user_email])
+                if curs.fetchall():
+                    for i in re_set_list:
+                        flask.session.pop(i, None)
+        
+                    return re_error('/error/35')
+                
+                if send_email(user_email, t_text, i_text) == 0:
+                    for i in re_set_list:
+                        flask.session.pop(i, None)
+        
+                    return re_error('/error/18')
+        
+                flask.session['c_email'] = user_email
+        
+                return redirect('/pass_find/email')
+        else:
+            if tool == 'pass_find':
+                curs.execute(db_change('select data from other where name = "password_search_text"'))
+                sql_d = curs.fetchall()
+                b_text = (sql_d[0][0] + '<hr class="main_hr">') if sql_d and sql_d[0][0] != '' else ''
+        
+                return easy_minify(flask.render_template(skin_check(),
+                    imp = [load_lang('password_search'), wiki_set(), wiki_custom(), wiki_css(['(' + load_lang('email') + ')', 0])],
+                    data = b_text + '''
+                        <form method="post">
+                            <input placeholder="''' + load_lang('id') + '''" name="id" type="text">
+                            <hr class="main_hr">
+                            <input placeholder="''' + load_lang('email') + '''" name="email" type="text">
+                            <hr class="main_hr">
+                            <button type="submit">''' + load_lang('save') + '''</button>
+                        </form>
+                    ''',
+                    menu = [['user', load_lang('return')]]
+                ))
+            else:
+                if tool == 'need_email' and not 'c_type' in flask.session:
+                    return redirect('/register')
+        
+                curs.execute(db_change('select data from other where name = "email_insert_text"'))
+                sql_d = curs.fetchall()
+                b_text = (sql_d[0][0] + '<hr class="main_hr">') if sql_d and sql_d[0][0] != '' else ''
+        
+                return easy_minify(flask.render_template(skin_check(),
+                    imp = [load_lang('email'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
+                    data = '''
+                        <a href="/email_filter">(''' + load_lang('email_filter_list') + ''')</a>
+                        <hr class="main_hr">
+                        ''' + b_text + '''
+                        <form method="post">
+                            <input placeholder="''' + load_lang('email') + '''" name="email" type="text">
+                            <hr class="main_hr">
+                            <button type="submit">''' + load_lang('save') + '''</button>
+                        </form>
+                    ''',
+                    menu = [['user', load_lang('return')]]
+                ))

+ 172 - 0
route/login_find_email_check.py

@@ -0,0 +1,172 @@
+from .tool.func import *
+
+# 개편 필요
+def login_find_email_check(tool):
+    with get_db_connect() as conn:
+        curs = conn.cursor()
+        
+        if  flask.request.method == 'POST' or \
+            ('c_key' in flask.session and flask.session['c_key'] == 'email_pass'):
+            re_set_list = ['c_id', 'c_pw', 'c_ans', 'c_que', 'c_key', 'c_type', 'c_email']
+            ip = ip_check()
+            input_key = flask.request.form.get('key', '')
+            user_agent = flask.request.headers.get('User-Agent', '')
+        
+            if  'c_type' in flask.session and \
+                flask.session['c_type'] == 'pass_find' and \
+                flask.session['c_key'] == input_key:
+                curs.execute(db_change("update user_set set data = ? where name = 'pw' and id = ?"), [
+                    pw_encode(flask.session['c_key']), 
+                    flask.session['c_id']
+                ])
+                
+                curs.execute(db_change('select data from user_set where name = "2fa" and id = ?'), [user_id])
+                if curs.fetchall():
+                    curs.execute(db_change("update user_set set data = '' where name = '2fa' and id = ?"), [user_id])
+        
+                user_id = flask.session['c_id']
+                user_pw = flask.session['c_key']
+        
+                for i in re_set_list:
+                    flask.session.pop(i, None)
+        
+                curs.execute(db_change('select data from other where name = "reset_user_text"'))
+                sql_d = curs.fetchall()
+                b_text = (sql_d[0][0] + '<hr class="main_hr">') if sql_d and sql_d[0][0] != '' else ''
+
+                conn.commit()
+        
+                return easy_minify(flask.render_template(skin_check(),
+                    imp = [load_lang('reset_user_ok'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
+                    data = '' + \
+                        b_text + \
+                        load_lang('id') + ' : ' + user_id + \
+                        '<hr class="main_hr">' + \
+                        load_lang('password') + ' : ' + user_pw + \
+                    '',
+                    menu = [['user', load_lang('return')]]
+                ))
+            elif    'c_type' in flask.session and \
+                    (flask.session['c_key'] == input_key or flask.session['c_key'] == 'email_pass'):
+                curs.execute(db_change('select data from other where name = "encode"'))
+                db_data = curs.fetchall()
+        
+                if flask.session['c_type'] == 'register':
+                    if flask.session['c_key'] == 'email_pass':
+                        flask.session['c_email'] = ''
+        
+                    curs.execute(db_change("select id from user_set limit 1"))
+                    first = 1 if not curs.fetchall() else 0
+        
+                    curs.execute(db_change("select id from user_set where id = ?"), [
+                        flask.session['c_id']
+                    ])
+                    if curs.fetchall():
+                        for i in re_set_list:
+                            flask.session.pop(i, None)
+        
+                        return re_error('/error/6')
+                
+                    curs.execute(db_change("select id from user_set where id = ? and name = 'application'"), [
+                        flask.session['c_id']
+                    ])
+                    if curs.fetchall():
+                        for i in re_set_list:
+                            flask.session.pop(i, None)
+        
+                        return re_error('/error/6')
+        
+                    curs.execute(db_change(
+                        'select data from other where name = "requires_approval"'
+                    ))
+                    requires_approval = curs.fetchall()
+                    if requires_approval and requires_approval[0][0] == 'on':
+                        user_app_data = {}
+                        user_app_data['id'] = flask.session['c_id']
+                        user_app_data['pw'] = flask.session['c_pw']
+                        user_app_data['date'] = get_time()
+                        user_app_data['encode'] = db_data[0][0]
+                        user_app_data['question'] = flask.session['c_que']
+                        user_app_data['answer'] = flask.session['c_ans']
+                        user_app_data['ip'] = ip
+                        user_app_data['ua'] = user_agent
+                        user_app_data['email'] = flask.session['c_email']
+                        
+                        curs.execute(db_change(
+                            "insert into user_set (id, name, data) values (?, ?, ?)"
+                        ), [
+                            flask.session['c_id'],
+                            'application',
+                            json.dumps(user_app_data)
+                        ])
+                        conn.commit()
+        
+                        for i in re_set_list:
+                            flask.session.pop(i, None)
+        
+                        return redirect('/application_submitted')
+                    else:
+                        if first == 0:
+                            user_auth = 'user'
+                        else:
+                            user_auth = 'owner'
+                        
+                        curs.execute(db_change("insert into user_set (id, name, data) values (?, 'pw', ?)"), [
+                            flask.session['c_id'],
+                            flask.session['c_pw']
+                        ])
+                        curs.execute(db_change("insert into user_set (id, name, data) values (?, 'acl', ?)"), [
+                            flask.session['c_id'],
+                            user_auth
+                        ])
+                        curs.execute(db_change("insert into user_set (id, name, data) values (?, 'date', ?)"), [
+                            flask.session['c_id'],
+                            get_time()
+                        ])
+                        curs.execute(db_change("insert into user_set (id, name, data) values (?, 'encode', ?)"), [
+                            flask.session['c_id'],
+                            db_data[0][0]
+                        ])
+        
+                    curs.execute(db_change("insert into user_set (name, id, data) values ('email', ?, ?)"), [
+                        flask.session['c_id'],
+                        flask.session['c_email']
+                    ])
+                    ua_plus(flask.session['c_id'], ip, user_agent, get_time())
+        
+                    flask.session['id'] = flask.session['c_id']
+                    flask.session['head'] = ''
+        
+                    conn.commit()
+                else:
+                    curs.execute(db_change('delete from user_set where name = "email" and id = ?'), [ip])
+                    curs.execute(db_change('insert into user_set (name, id, data) values ("email", ?, ?)'), [ip, flask.session['c_email']])
+        
+                    first = 0
+        
+                for i in re_set_list:
+                    flask.session.pop(i, None)
+        
+                return redirect('/change') if first == 0 else redirect('/setting') 
+            else:
+                for i in re_set_list:
+                    flask.session.pop(i, None)
+        
+                return redirect('/user')
+        else:
+            curs.execute(db_change('select data from other where name = "check_key_text"'))
+            sql_d = curs.fetchall()
+            b_text = (sql_d[0][0] + '<hr class="main_hr">') if sql_d and sql_d[0][0] != '' else ''
+        
+            return easy_minify(flask.render_template(skin_check(),
+                imp = [load_lang('check_key'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
+                data = '''
+                    <form method="post">
+                        ''' + b_text + '''
+                        <input placeholder="''' + load_lang('key') + '''" name="key" type="password">
+                        <hr class="main_hr">
+                        <button type="submit">''' + load_lang('save') + '''</button>
+                    </form>
+                ''',
+                menu = [['user', load_lang('return')]]
+            ))

+ 56 - 0
route/login_find_key.py

@@ -0,0 +1,56 @@
+from .tool.func import *
+
+def login_find_key():
+    with get_db_connect() as conn:
+        curs = conn.cursor()
+        if flask.request.method == 'POST':
+            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)
+            
+            input_key = flask.request.form.get('key', '')
+            curs.execute(db_change('select id from user_set where name = "random_key" and data = ?'), [input_key])
+            db_data = curs.fetchall()
+            if not db_data:
+                return redirect('/user')
+            else:
+                user_id = db_data[0][0]
+            
+            key = load_random_key(32)
+            curs.execute(db_change("update user_set set data = ? where name = 'pw' and id = ?"), [
+                pw_encode(key), 
+                user_id
+            ])
+            
+            curs.execute(db_change('select data from user_set where name = "2fa" and id = ?'), [user_id])
+            if curs.fetchall():
+                curs.execute(db_change("update user_set set data = '' where name = '2fa' and id = ?"), [user_id])
+            
+            curs.execute(db_change('select data from other where name = "reset_user_text"'))
+            sql_d = curs.fetchall()
+            b_text = (sql_d[0][0] + '<hr class="main_hr">') if sql_d and sql_d[0][0] != '' else ''
+            
+            return easy_minify(flask.render_template(skin_check(),
+                    imp = [load_lang('reset_user_ok'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
+                    data = '' + \
+                        b_text + \
+                        load_lang('id') + ' : ' + user_id + \
+                        '<hr class="main_hr">' + \
+                        load_lang('password') + ' : ' + key + \
+                    '',
+                    menu = [['user', load_lang('return')]]
+                ))
+        else:
+            return easy_minify(flask.render_template(skin_check(),
+                imp = [load_lang('password_search'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
+                data = '''
+                    <form method="post">
+                        <input placeholder="''' + load_lang('key') + '''" name="key" type="password">
+                        <hr class="main_hr">
+                        ''' + captcha_get() + '''
+                        <button type="submit">''' + load_lang('send') + '''</button>
+                    </form>
+                ''',
+                menu = [['user', load_lang('return')]]
+            ))

+ 8 - 5
route/login_login.py

@@ -26,9 +26,12 @@ def login_login_2(conn):
         sql_data = curs.fetchall()
         if not sql_data:
             return re_error('/error/2')
-        else:
-            for i in sql_data:
-                user_data[i[0]] = i[1]
+
+        for i in sql_data:
+            user_data[i[0]] = i[1]
+                
+        if len(user_data) < 2:
+            return re_error('/error/2')
 
         if pw_check(
             flask.request.form.get('pw', ''),
@@ -57,9 +60,9 @@ def login_login_2(conn):
             data =  '''
                     <form method="post">
                         <input placeholder="''' + load_lang('id') + '''" name="id" type="text">
-                        <hr class=\"main_hr\">
+                        <hr class="main_hr">
                         <input placeholder="''' + load_lang('password') + '''" name="pw" type="password">
-                        <hr class=\"main_hr\">
+                        <hr class="main_hr">
                         ''' + captcha_get() + '''
                         <button type="submit">''' + load_lang('login') + '''</button>
                         ''' + http_warning() + '''

+ 0 - 115
route/login_pass_find.py

@@ -1,115 +0,0 @@
-from .tool.func import *
-
-# 개편 필요
-def login_pass_find_2(conn, tool):
-    curs = conn.cursor()
-    
-    if flask.request.method == 'POST':
-        re_set_list = ['c_id', 'c_pw', 'c_ans', 'c_que', 'c_key', 'c_type']
-    
-        if tool == 'email_change':
-            flask.session['c_key'] = load_random_key(32)
-            flask.session['c_id'] = ip_check()
-            flask.session['c_type'] = 'email_change'
-        elif tool == 'pass_find':
-            user_id = flask.request.form.get('id', '')
-            user_email = flask.request.form.get('email', '')
-
-            flask.session['c_key'] = load_random_key(32)
-            flask.session['c_id'] = user_id
-            flask.session['c_type'] = 'pass_find'
-        else:
-            if not 'c_type' in flask.session:
-                return redirect('/register')
-
-        if tool != 'pass_find':
-            user_email = flask.request.form.get('email', '')
-            email_data = re.search(r'@([^@]+)$', user_email)
-            if email_data:
-                curs.execute(db_change("select html from html_filter where html = ? and kind = 'email'"), [email_data.group(1)])
-                if not curs.fetchall():
-                    for i in re_set_list:
-                        flask.session.pop(i, None)
-                    
-                    return redirect('/email_filter')
-            else:
-                for i in re_set_list:
-                    flask.session.pop(i, None)
-                
-                return re_error('/error/36')
-
-        curs.execute(db_change('select data from other where name = "email_title"'))
-        sql_d = curs.fetchall()
-        t_text = html.escape(sql_d[0][0]) if sql_d and sql_d[0][0] != '' else (wiki_set()[0] + ' key')
-
-        curs.execute(db_change('select data from other where name = "email_text"'))
-        sql_d = curs.fetchall()
-        i_text = (html.escape(sql_d[0][0]) + '\n\nKey : ' + flask.session['c_key']) if sql_d and sql_d[0][0] != '' else ('Key : ' + flask.session['c_key'])
-        
-        if tool == 'pass_find':
-            curs.execute(db_change("select id from user_set where id = ? and name = 'email' and data = ?"), [user_id, user_email])
-            if not curs.fetchall():
-                return re_error('/error/12')
-                
-            if send_email(user_email, t_text, i_text) == 0:
-                return re_error('/error/18')
-
-            return redirect('/pass_find/email')
-        else:
-            curs.execute(db_change('select id from user_set where name = "email" and data = ?'), [user_email])
-            if curs.fetchall():
-                for i in re_set_list:
-                    flask.session.pop(i, None)
-
-                return re_error('/error/35')
-            
-            if send_email(user_email, t_text, i_text) == 0:
-                for i in re_set_list:
-                    flask.session.pop(i, None)
-
-                return re_error('/error/18')
-
-            flask.session['c_email'] = user_email
-
-            return redirect('/pass_find/email')
-    else:
-        if tool == 'pass_find':
-            curs.execute(db_change('select data from other where name = "password_search_text"'))
-            sql_d = curs.fetchall()
-            b_text = (sql_d[0][0] + '<hr class="main_hr">') if sql_d and sql_d[0][0] != '' else ''
-
-            return easy_minify(flask.render_template(skin_check(),
-                imp = [load_lang('password_search'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
-                data = b_text + '''
-                    <form method="post">
-                        <input placeholder="''' + load_lang('id') + '''" name="id" type="text">
-                        <hr class="main_hr">
-                        <input placeholder="''' + load_lang('email') + '''" name="email" type="text">
-                        <hr class="main_hr">
-                        <button type="submit">''' + load_lang('save') + '''</button>
-                    </form>
-                ''',
-                menu = [['user', load_lang('return')]]
-            ))
-        else:
-            if tool == 'need_email' and not 'c_type' in flask.session:
-                return redirect('/register')
-
-            curs.execute(db_change('select data from other where name = "email_insert_text"'))
-            sql_d = curs.fetchall()
-            b_text = (sql_d[0][0] + '<hr class="main_hr">') if sql_d and sql_d[0][0] != '' else ''
-
-            return easy_minify(flask.render_template(skin_check(),
-                imp = [load_lang('email'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
-                data = '''
-                    <a href="/email_filter">(''' + load_lang('email_filter_list') + ''')</a>
-                    <hr class="main_hr">
-                    ''' + b_text + '''
-                    <form method="post">
-                        <input placeholder="''' + load_lang('email') + '''" name="email" type="text">
-                        <hr class="main_hr">
-                        <button type="submit">''' + load_lang('save') + '''</button>
-                    </form>
-                ''',
-                menu = [['user', load_lang('return')]]
-            ))

+ 0 - 164
route/login_pass_find_email.py

@@ -1,164 +0,0 @@
-from .tool.func import *
-
-def login_pass_find_email_2(conn, tool):
-    curs = conn.cursor()
-
-    if  flask.request.method == 'POST' or \
-        ('c_key' in flask.session and flask.session['c_key'] == 'email_pass'):
-        re_set_list = ['c_id', 'c_pw', 'c_ans', 'c_que', 'c_key', 'c_type', 'c_email']
-        ip = ip_check()
-        input_key = flask.request.form.get('key', '')
-        user_agent = flask.request.headers.get('User-Agent', '')
-
-        if  'c_type' in flask.session and \
-            flask.session['c_type'] == 'pass_find' and \
-            flask.session['c_key'] == input_key:
-            curs.execute(db_change("update user_set set data = ? where name = 'pw' and id = ?"), [
-                pw_encode(flask.session['c_key']), 
-                flask.session['c_id']
-            ])
-            conn.commit()
-
-            user_id = flask.session['c_id']
-            user_pw = flask.session['c_key']
-
-            for i in re_set_list:
-                flask.session.pop(i, None)
-
-            curs.execute(db_change('select data from other where name = "reset_user_text"'))
-            sql_d = curs.fetchall()
-            b_text = (sql_d[0][0] + '<hr class="main_hr">') if sql_d and sql_d[0][0] != '' else ''
-
-            curs.execute(db_change('select data from user_set where name = "2fa" and id = ?'), [user_id])
-            if curs.fetchall():
-                curs.execute(db_change("update user_set set data = '' where name = '2fa' and id = ?"), [user_id])
-
-            return easy_minify(flask.render_template(skin_check(),
-                imp = [load_lang('reset_user_ok'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
-                data = b_text + load_lang('id') + ' : ' + user_id + '<br>' + load_lang('password') + ' : ' + user_pw,
-                menu = [['user', load_lang('return')]]
-            ))
-        elif    'c_type' in flask.session and \
-                (flask.session['c_key'] == input_key or flask.session['c_key'] == 'email_pass'):
-            curs.execute(db_change('select data from other where name = "encode"'))
-            db_data = curs.fetchall()
-
-            if flask.session['c_type'] == 'register':
-                if flask.session['c_key'] == 'email_pass':
-                    flask.session['c_email'] = ''
-
-                curs.execute(db_change("select id from user_set limit 1"))
-                first = 1 if not curs.fetchall() else 0
-
-                curs.execute(db_change("select id from user_set where id = ?"), [
-                    flask.session['c_id']
-                ])
-                if curs.fetchall():
-                    for i in re_set_list:
-                        flask.session.pop(i, None)
-
-                    return re_error('/error/6')
-            
-                curs.execute(db_change("select id from user_set where id = ? and name = 'application'"), [
-                    flask.session['c_id']
-                ])
-                if curs.fetchall():
-                    for i in re_set_list:
-                        flask.session.pop(i, None)
-
-                    return re_error('/error/6')
-
-                curs.execute(db_change(
-                    'select data from other where name = "requires_approval"'
-                ))
-                requires_approval = curs.fetchall()
-                if requires_approval and requires_approval[0][0] == 'on':
-                    user_app_data = {}
-                    user_app_data['id'] = flask.session['c_id']
-                    user_app_data['pw'] = flask.session['c_pw']
-                    user_app_data['date'] = get_time()
-                    user_app_data['encode'] = db_data[0][0]
-                    user_app_data['question'] = flask.session['c_que']
-                    user_app_data['answer'] = flask.session['c_ans']
-                    user_app_data['ip'] = ip
-                    user_app_data['ua'] = user_agent
-                    user_app_data['email'] = flask.session['c_email']
-                    
-                    curs.execute(db_change(
-                        "insert into user_set (id, name, data) values (?, ?, ?)"
-                    ), [
-                        flask.session['c_id'],
-                        'application',
-                        json.dumps(user_app_data)
-                    ])
-                    conn.commit()
-    
-                    for i in re_set_list:
-                        flask.session.pop(i, None)
-
-                    return redirect('/application_submitted')
-                else:
-                    if first == 0:
-                        user_auth = 'user'
-                    else:
-                        user_auth = 'owner'
-                    
-                    curs.execute(db_change("insert into user_set (id, name, data) values (?, 'pw', ?)"), [
-                        flask.session['c_id'],
-                        flask.session['c_pw']
-                    ])
-                    curs.execute(db_change("insert into user_set (id, name, data) values (?, 'acl', ?)"), [
-                        flask.session['c_id'],
-                        user_auth
-                    ])
-                    curs.execute(db_change("insert into user_set (id, name, data) values (?, 'date', ?)"), [
-                        flask.session['c_id'],
-                        get_time()
-                    ])
-                    curs.execute(db_change("insert into user_set (id, name, data) values (?, 'encode', ?)"), [
-                        flask.session['c_id'],
-                        db_data[0][0]
-                    ])
-
-                curs.execute(db_change("insert into user_set (name, id, data) values ('email', ?, ?)"), [
-                    flask.session['c_id'],
-                    flask.session['c_email']
-                ])
-                ua_plus(flask.session['c_id'], ip, user_agent, get_time())
-
-                flask.session['id'] = flask.session['c_id']
-                flask.session['head'] = ''
-
-                conn.commit()
-            else:
-                curs.execute(db_change('delete from user_set where name = "email" and id = ?'), [ip])
-                curs.execute(db_change('insert into user_set (name, id, data) values ("email", ?, ?)'), [ip, flask.session['c_email']])
-
-                first = 0
-
-            for i in re_set_list:
-                flask.session.pop(i, None)
-
-            return redirect('/change') if first == 0 else redirect('/setting') 
-        else:
-            for i in re_set_list:
-                flask.session.pop(i, None)
-
-            return redirect('/user')
-    else:
-        curs.execute(db_change('select data from other where name = "check_key_text"'))
-        sql_d = curs.fetchall()
-        b_text = (sql_d[0][0] + '<hr class="main_hr">') if sql_d and sql_d[0][0] != '' else ''
-
-        return easy_minify(flask.render_template(skin_check(),
-            imp = [load_lang('check_key'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
-            data = '''
-                <form method="post">
-                    ''' + b_text + '''
-                    <input placeholder="''' + load_lang('key') + '''" name="key" type="text">
-                    <hr class="main_hr">
-                    <button type="submit">''' + load_lang('save') + '''</button>
-                </form>
-            ''',
-            menu = [['user', load_lang('return')]]
-        ))

+ 2 - 2
route/login_register_submit.py

@@ -44,7 +44,7 @@ def login_register_submit_2(conn):
         ])
         conn.commit()
         
-        return redirect('/app_submit')
+        return redirect('/')
     else:
         return easy_minify(flask.render_template(skin_check(),
             imp = [load_lang('approval_question'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
@@ -58,4 +58,4 @@ def login_register_submit_2(conn):
                 </form>
             ''',
             menu = [['user', load_lang('return')]]
-        ))
+        ))

+ 2 - 2
route/main_error_404.py

@@ -1,7 +1,7 @@
 from .tool.func import *
 
-def main_error_404(e):
+def main_error_404(e = ''):
     if os.path.exists('404.html') and flask.request.path != '/':
-        return open('404.html', encoding='utf8').read()
+        return open('404.html', encoding = 'utf8').read(), 404
     else:
         return redirect('/w/' + wiki_set(2))

+ 29 - 10
route/main_func_setting.py

@@ -243,7 +243,8 @@ def main_func_setting(db_set, num = 0):
                 'edit_help',
                 'upload_help',
                 'upload_default',
-                'license'
+                'license',
+                'topic_text'
             ]
             if flask.request.method == 'POST':
                 for i in i_list:
@@ -328,6 +329,9 @@ def main_func_setting(db_set, num = 0):
 
                             <h2>17. ''' + load_lang('bottom_text') + ''' (HTML)</h2>
                             <textarea rows="3" name="''' + i_list[16] + '''">''' + html.escape(d_list[16]) + '''</textarea>
+                            
+                            <h2>18. ''' + load_lang('topic_text') + '''</h2>
+                            <textarea rows="3" name="''' + i_list[17] + '''">''' + html.escape(d_list[17]) + '''</textarea>
 
                             <hr class="main_hr">
                             <button id="save" type="submit">''' + load_lang('save') + '''</button>
@@ -532,20 +536,29 @@ def main_func_setting(db_set, num = 0):
 
                 security_radios = ''
                 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">'
+                    if d_list[4] == i:
+                        security_radios = '<option value="' + i + '">' + i + '</option>' + security_radios
+                    else:
+                        security_radios += '<option value="' + i + '">' + i + '</option>'
 
+                re_ver_list = {
+                    '' : 'reCAPTCHA v2',
+                    'v3' : 'reCAPTCHA v3',
+                    'h' : 'hCAPTCHA'
+                }
                 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>'
+                for i in re_ver_list:
+                    if d_list[7] == i:
+                        re_ver = '<option value="' + i + '">' + re_ver_list[i] + '</option>' + re_ver
+                    else:
+                        re_ver += '<option value="' + i + '">' + re_ver_list[i] + '</option>'
 
                 return easy_minify(flask.render_template(skin_check(),
                     imp = [load_lang('ext_api_req_set'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
                     data = '''
                         <form method="post" id="main_set_data">
-                            <h2>1. ''' + load_lang('recaptcha') + '''</h2>
-                            <a href="https://www.google.com/recaptcha/admin">(Google)</a>
+                            <h2>1. ''' + load_lang('captcha') + '''</h2>
+                            <a href="https://www.google.com/recaptcha/">(''' + load_lang('recaptcha') + ''')</a> <a href="https://www.hcaptcha.com/">(''' + load_lang('hcaptcha') + ''')</a>
                             <hr class="main_hr">
 
                             <span>''' + load_lang('public_key') + '''</span>
@@ -558,6 +571,8 @@ def main_func_setting(db_set, num = 0):
                             <input name="sec_re" value="''' + html.escape(d_list[1]) + '''">
                             <hr class="main_hr">
 
+                            <span>''' + load_lang('version') + '''</span>
+                            <hr class="main_hr">
                             <select name="recaptcha_ver">
                                 ''' + re_ver + '''
                             </select>
@@ -582,7 +597,9 @@ def main_func_setting(db_set, num = 0):
 
                             <span>''' + load_lang('smtp_security') + '''</span>
                             <hr class="main_hr">
-                            ''' + security_radios + '''
+                            <select name="recaptcha_ver">
+                                ''' + security_radios + '''
+                            </select>
                             <hr class="main_hr">
 
                             <span>''' + load_lang('smtp_username') + '''</span>
@@ -594,7 +611,7 @@ def main_func_setting(db_set, num = 0):
                             <hr class="main_hr">
                             <input type="password" name="smtp_pass" value="''' + html.escape(d_list[6]) + '''">
 
-                            <h2>3. ''' + load_lang('oauth') + '''</h2>
+                            <h2>3. ''' + load_lang('oauth') + ''' (''' + load_lang('incomplete') + ''')</h2>
                             <a href="https://developers.google.com/identity/protocols/oauth2">(Google)</a>
                             <hr class="main_hr">
 
@@ -732,6 +749,8 @@ def main_func_setting(db_set, num = 0):
                         curs.execute(db_change('insert into other (name, data, coverage) values (?, ?, ?)'), [i[0], '', i[1]])
 
                         d_list += ['']
+                        
+                conn.commit()
 
                 end_data = ''
                 for i in range(0, len(skin_list)):

+ 1 - 1
route/main_func_skin_set.py

@@ -1,6 +1,6 @@
 from .tool.func import *
 
-def main_func_skin_set(conn):
+def main_func_skin_set():
     with get_db_connect() as conn:
         curs = conn.cursor()
 

+ 7 - 7
route/main_tool_admin.py

@@ -1,6 +1,6 @@
 from .tool.func import *
 
-def main_tool_admin(num = 1, add_1 = '', add_2 = ''):
+def main_tool_admin(num = 1, add_2 = ''):
     title_list = {
         0 : [load_lang('document_name'), 'acl', load_lang('acl')],
         1 : [0, 'check', load_lang('check')],
@@ -52,7 +52,7 @@ def main_tool_admin(num = 1, add_1 = '', add_2 = ''):
                         <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>
-                        <li><a href="/doc_filter">''' + load_lang('doc_filter_list') + '''</a></li>
+                        <li><a href="/filter/document/list">''' + load_lang('document_filter_list') + '''</a></li>
                     </ul>
                     <h3>2.2. ''' + load_lang('server') + '''</h2>
                     <ul class="inside_ul">
@@ -77,17 +77,17 @@ def main_tool_admin(num = 1, add_1 = '', add_2 = ''):
         ))
     elif not num - 1 > len(title_list):
         num -= 2
+        
+        add_1 = flask.request.form.get('name', 'test')
         if flask.request.method == 'POST':
             if add_2 != '':
-                return redirect(
-                    '/' + title_list[num][1] + '/' + url_pas(add_2) + '?plus=' + url_pas(add_1)
-                )
-            elif flask.request.form.get('regex', None):
+                return redirect('/' + title_list[num][1] + '/' + url_pas(add_2) + '/doc_from/' + url_pas(add_1))
+            elif flask.request.form.get('regex', '') != '':
                 return redirect('/' + title_list[num][1] + '/' + url_pas(add_1) + '?type=regex')
             else:
                 return redirect('/' + title_list[num][1] + '/' + url_pas(add_1))
         else:
-            if title_list[(num - 2)][0] == 0:
+            if title_list[num][0] == 0:
                 placeholder = load_lang('user_name')
             else:
                 placeholder = title_list[num][0]

+ 1 - 1
route/recent_history_tool.py

@@ -34,7 +34,7 @@ def recent_history_tool_2(conn, name, rev):
         else:
             data += load_lang('hide')
             
-        data += '</li>'
+        data += '</a></li>'
         data += '</ul>'
 
     if admin_check() == 1:

+ 262 - 81
route/tool/func.py

@@ -65,6 +65,7 @@ from .func_mark import *
 from diff_match_patch import diff_match_patch
 
 import netius.servers
+import waitress
 
 import werkzeug.routing
 import werkzeug.debug
@@ -77,14 +78,14 @@ import pymysql
 
 if sys.version_info < (3, 6):
     import sha3
-   
+
 # Init-Global
 global_lang = {}
 global_wiki_set = {}
 
 global_db_set = ''
 
-data_css_ver = '119'
+data_css_ver = '123'
 data_css = ''
 
 conn = ''
@@ -109,7 +110,12 @@ class get_db_connect_old:
         
     def db_load(self):
         if self.db_set['type'] == 'sqlite':
-            self.conn = sqlite3.connect(self.db_set['name'] + '.db')
+            self.conn = sqlite3.connect(
+                self.db_set['name'] + '.db',
+                check_same_thread = False,
+                isolation_level = None
+            )
+            self.conn.execute('pragma journal_mode = wal')
         else:
             self.conn = pymysql.connect(
                 host = self.db_set['mysql_host'],
@@ -133,7 +139,7 @@ class get_db_connect_old:
         load_conn(self.conn)
 
         return self.conn
-    
+
     def db_get(self):
         # if self.db_set['type'] != 'sqlite':
         #     self.conn.ping(reconnect = True)
@@ -141,14 +147,28 @@ class get_db_connect_old:
         return self.conn
     
 class get_db_connect:
+    # 임시 DB 커넥션 동작 구조
+    # Init 파트
+    # DB 커넥트(get_db_connect_old) -> func.py로 conn 넘겨줌
+    # route 파트
+    # DB 새로 커넥트 -> func.py에서 쓰던 conn은 conn_sub로 보관 ->
+    # func.py로 conn 넘겨줌 -> 모든 라우터 과정이 끝나면 conn_sub를 다시 func.py에 conn으로 넘겨줌 ->
+    # DB 커넥트 종료
     def __init__(self):
         global global_db_set
+        global conn
         
+        self.conn_sub = conn
         self.db_set = global_db_set
         
     def __enter__(self):
         if self.db_set['type'] == 'sqlite':
-            self.conn = sqlite3.connect(self.db_set['name'] + '.db')
+            self.conn = sqlite3.connect(
+                self.db_set['name'] + '.db',
+                check_same_thread = False,
+                isolation_level = None
+            )
+            self.conn.execute('pragma journal_mode = wal')
         else:
             self.conn = pymysql.connect(
                 host = self.db_set['mysql_host'],
@@ -169,11 +189,137 @@ class get_db_connect:
 
             self.conn.select_db(self.db_set['name'])
 
+        load_conn(self.conn)
         return self.conn
     
     def __exit__(self, exc_type, exc_value, traceback):
+        load_conn(self.conn_sub)
         self.conn.close()
 
+class class_check_json:
+    def do_check_set_json():
+        if os.getenv('NAMU_DB') or os.getenv('NAMU_DB_TYPE'):
+            set_data = {}
+            set_data['db'] = os.getenv('NAMU_DB') if os.getenv('NAMU_DB') else 'data'
+            set_data['db'] = os.getenv('NAMU_DB_TYPE') if os.getenv('NAMU_DB_TYPE') else 'sqlite'
+        else:
+            if os.path.exists(os.path.join('data', 'set.json')):
+                db_set_list = ['db', 'db_type']
+                set_data = json.loads(open(
+                    os.path.join('data', 'set.json'), 
+                    encoding = 'utf8'
+                ).read())
+                for i in db_set_list:
+                    if not i in set_data:
+                        os.remove(os.path.join('data', 'set.json'))
+                        
+                        break
+            
+            if not os.path.exists(os.path.join('data', 'set.json')):
+                set_data = {}
+                normal_db_type = ['sqlite', 'mysql']
+
+                print('DB type (' + normal_db_type[0] + ') [' + ', '.join(normal_db_type) + '] : ', end = '')
+                data_get = str(input())
+                if data_get == '' or not data_get in normal_db_type:
+                    set_data['db_type'] = 'sqlite'
+                else:
+                    set_data['db_type'] = data_get
+
+                all_src = []
+                if set_data['db_type'] == 'sqlite':
+                    for i_data in os.listdir("."):
+                        f_src = re.search(r"(.+)\.db$", i_data)
+                        if f_src:
+                            all_src += [f_src.group(1)]
+
+                print('DB name (data) [' + ', '.join(all_src) + '] : ', end = '')
+
+                data_get = str(input())
+                if data_get == '':
+                    set_data['db'] = 'data'
+                else:
+                    set_data['db'] = data_get
+
+                with open(os.path.join('data', 'set.json'), 'w', encoding = 'utf8') as f:
+                    f.write(json.dumps(set_data))
+
+        print('DB name : ' + set_data['db'])
+        print('DB type : ' + set_data['db_type'])
+        
+        data_db_set = {}
+        data_db_set['name'] = set_data['db']
+        data_db_set['type'] = set_data['db_type']
+
+        return data_db_set
+
+    def do_check_mysql_json(data_db_set):
+        if os.path.exists(os.path.join('data', 'mysql.json')):
+            db_set_list = ['user', 'password', 'host', 'port']
+            set_data = json.loads(
+                open(
+                    os.path.join('data', 'mysql.json'),
+                    encoding = 'utf8'
+                ).read()
+            )
+            for i in db_set_list:
+                if not i in set_data:
+                    os.remove(os.path.join('data', 'mysql.json'))
+                    
+                    break
+
+            set_data_mysql = set_data
+
+        if not os.path.exists(os.path.join('data', 'mysql.json')):
+            set_data_mysql = {}
+
+            print('DB user ID : ', end = '')
+            set_data_mysql['user'] = str(input())
+
+            print('DB password : ', end = '')
+            set_data_mysql['password'] = str(input())
+
+            print('DB host (localhost) : ', end = '')
+            set_data_mysql['host'] = str(input())
+            if set_data_mysql['host'] == '':
+                set_data_mysql['host'] = 'localhost'
+
+            print('DB port (3306) : ', end = '')
+            set_data_mysql['port'] = str(input())
+            if set_data_mysql['port'] == '':
+                set_data_mysql['port'] = '3306'
+
+            with open(
+                os.path.join('data', 'mysql.json'), 
+                'w', 
+                encoding = 'utf8'
+            ) as f:
+                f.write(json.dumps(set_data_mysql))
+
+        data_db_set['mysql_user'] = set_data_mysql['user']
+        data_db_set['mysql_pw'] = set_data_mysql['password']
+        if 'host' in set_data_mysql:
+            data_db_set['mysql_host'] = set_data_mysql['host']
+        else:
+            data_db_set['mysql_host'] = 'localhost'
+
+        if 'port' in set_data_mysql:
+            data_db_set['mysql_port'] = set_data_mysql['port']
+        else:
+            data_db_set['mysql_port'] = '3306'
+            
+        return data_db_set
+    
+    def __init__(self):
+        self.data_db_set = {}
+            
+    def __new__(self):
+        self.data_db_set = self.do_check_set_json()
+        if self.data_db_set['type'] == 'mysql':
+            self.data_db_set = self.do_check_mysql_json(self.data_db_set)
+        
+        return self.data_db_set
+
 def update(ver_num, set_data):
     curs = conn.cursor()
 
@@ -453,7 +599,7 @@ def set_init():
 def get_default_admin_group():
     return ['owner', 'ban']
 
-def load_random_key(long = 64):
+def load_random_key(long = 128):
     return ''.join(
         random.choice(
             "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
@@ -1018,15 +1164,14 @@ def captcha_get():
         
         curs.execute(db_change('select data from other where name = "recaptcha_ver"'))
         rec_ver = curs.fetchall()
-        if  recaptcha and recaptcha[0][0] != '' and \
-            sec_re and sec_re[0][0] != '':
+        if recaptcha and recaptcha[0][0] != '' and sec_re and sec_re[0][0] != '':
             if not rec_ver or rec_ver[0][0] == '':
                 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:
+            elif rec_ver[0][0] == 'v3':
                 data += '' + \
                     '<script src="https://www.google.com/recaptcha/api.js?render=' + recaptcha[0][0] + '"></script>' + \
                     '<input type="hidden" id="g-recaptcha" name="g-recaptcha">' + \
@@ -1038,25 +1183,43 @@ def captcha_get():
                         '});' + \
                     '</script>' + \
                 ''
+            else:
+                data += '''
+                    <script src="https://js.hcaptcha.com/1/api.js" async defer></script>
+                    <div class="h-captcha" data-sitekey="''' + recaptcha[0][0] + '''"></div>
+                    <hr class="main_hr">
+                '''
 
     return data
 
 def captcha_post(re_data, num = 1):
     curs = conn.cursor()
 
-    if num == 1:
+    if num == 1 and ip_or_user() != 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] != '' and \
-            ip_or_user() != 0 and captcha_get() != '':
-            data = requests.get(
-                'https://www.google.com/recaptcha/api/siteverify' + \
-                '?secret=' + sec_re[0][0] + '&response=' + re_data
-            )
-            if data.status_code == 200:
-                json_data = json.loads(data.text)
-                if json_data['success'] != True:
-                    return 1
+        
+        curs.execute(db_change('select data from other where name = "recaptcha_ver"'))
+        rec_ver = curs.fetchall()
+        if captcha_get() != '':
+            if not rec_ver or rec_ver[0][0] in ('', 'v3'):
+                data = requests.get(
+                    'https://www.google.com/recaptcha/api/siteverify' + \
+                    '?secret=' + sec_re[0][0] + '&response=' + re_data
+                )
+                if data.status_code == 200:
+                    json_data = json.loads(data.text)
+                    if json_data['success'] != True:
+                        return 1
+            else:
+                data = requests.get(
+                    'https://hcaptcha.com/siteverify' + \
+                    '?secret=' + sec_re[0][0] + '&response=' + re_data
+                )
+                if data.status_code == 200:
+                    json_data = json.loads(data.text)
+                    if json_data['success'] != True:
+                        return 1
 
         return 0
 
@@ -1142,44 +1305,42 @@ def acl_check(name = 'test', tool = '', topic_num = '1'):
 
     ip = ip_check()
     get_ban = ban_check()
-    acl_c = re.search(r"^user:((?:(?!\/).)*)", name) if name else None
-    if tool == '' and acl_c:
-        acl_n = acl_c.groups()
-
-        if get_ban == 1:
+    
+    if tool == '' and name:
+        if tool == '' and acl_check(name, 'render') == 1:
             return 1
-
-        if admin_check(5) == 1:
-            return 0
-
-        curs.execute(db_change(
-            "select data from acl where title = ? and type = 'decu'"
-        ), ['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:
+        
+        user_page = re.search(r"^user:((?:(?!\/).)*)", name)
+        if user_page:
+            user_page = user_page.group(1)
+            if admin_check(5) == 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:
+                
+            if get_ban == 1:
+                return 1
+                
+            curs.execute(db_change(
+                "select data from acl where title = ? and type = 'decu'"
+            ), [name])
+            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
+            
+            if ip == user_page and not ip_or_user(ip) == 1:
                 return 0
-
-        return 1
-
-    if tool == 'topic':
-        if not name:
-            curs.execute(db_change("select title from rd where code = ?"), [topic_num])
-            name = curs.fetchall()
-            name = name[0][0] if name else 'test'
-        
-        end = 3
-    elif tool == 'render' or tool == '' or tool == 'vote':
-        if tool == '' and acl_check(name, 'render') == 1:
+    
             return 1
+    elif tool == 'topic':
+        curs.execute(db_change("select title from rd where code = ?"), [topic_num])
+        name = curs.fetchall()
+        name = name[0][0] if name else 'test'
 
+    if tool in ['topic']:
+        end = 3
+    elif tool in ['render', 'vote', '']:
         end = 2
     else:
         end = 1
@@ -1190,27 +1351,43 @@ def acl_check(name = 'test', tool = '', topic_num = '1'):
                 curs.execute(db_change(
                     "select data from acl where title = ? and type = 'decu'"
                 ), [name])
+                '''
+            elif i == 1:
+                curs.execute(db_change(
+                    "select plus from html_filter where kind = 'document'"
+                ))
+                '''
             else:
-                curs.execute(db_change('select data from other where name = "edit"'))
+                curs.execute(db_change(
+                    'select data from other where name = "edit"'
+                ))
 
             num = 5
         elif tool == 'topic':
-            if i == 0 and topic_num:
-                curs.execute(db_change("select acl from rd where code = ?"), [topic_num])
+            if i == 0:
+                curs.execute(db_change(
+                    "select acl from rd where code = ?"
+                ), [topic_num])
             elif i == 1:
                 curs.execute(db_change(
                     "select data from acl where title = ? and type = 'dis'"
                 ), [name])
             else:
-                curs.execute(db_change('select data from other where name = "discussion"'))
+                curs.execute(db_change(
+                    'select data from other where name = "discussion"'
+                ))
 
             num = 3
         elif tool == 'upload':
-            curs.execute(db_change("select data from other where name = 'upload_acl'"))
+            curs.execute(db_change(
+                "select data from other where name = 'upload_acl'"
+            ))
 
             num = 5
         elif tool == 'many_upload':
-            curs.execute(db_change("select data from other where name = 'many_upload_acl'"))
+            curs.execute(db_change(
+                "select data from other where name = 'many_upload_acl'"
+            ))
 
             num = 5
         elif tool == 'vote':
@@ -1219,7 +1396,9 @@ def acl_check(name = 'test', tool = '', topic_num = '1'):
                     'select acl from vote where id = ? and user = ""'
                 ), [topic_num])
             else:
-                curs.execute(db_change('select data from other where name = "vote_acl"'))
+                curs.execute(db_change(
+                    'select data from other where name = "vote_acl"'
+                ))
 
             num = None
         else:
@@ -1234,18 +1413,16 @@ def acl_check(name = 'test', tool = '', topic_num = '1'):
             num = 5
 
         acl_data = curs.fetchall()
-        if  (
-                i == (end - 1) and \
-                (not acl_data or acl_data[0][0] == '' or acl_data[0][0] == 'normal')
-            ) 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':
+        if not acl_data:
+            acl_data = [['normal']]
+        elif acl_data and acl_data[0][0] == '':
+            acl_data = [['normal']]
+
+        if acl_data[0][0] != 'normal':
+            if not acl_data[0][0] in ['ban', 'ban_admin'] and get_ban == 1 and tool != 'render':
                 return 1
 
-            if acl_data[0][0] == 'all' or acl_data[0][0] == 'ban':
+            if acl_data[0][0] in ['all', 'ban']:
                 return 0
             elif acl_data[0][0] == 'user':
                 if ip_or_user(ip) != 1:
@@ -1311,23 +1488,27 @@ def acl_check(name = 'test', tool = '', topic_num = '1'):
                 if admin_check() == 1:
                     return 0
             elif acl_data[0][0] == 'ban_admin':
-                if admin_check(1) == 1 or ban_check() == 1:
+                if admin_check(1) == 1 or get_ban == 1:
                     return 0
 
             return 1
-        else:
-            if i == (end - 1):
-                if tool == 'topic' and 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:
+        elif i == (end - 1):
+            if get_ban == 1 and tool != 'render':
+                return 1
+            
+            if tool == 'topic':
+                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 1
                 else:
                     return 0
+            else:
+                return 0
 
     return 1
 

+ 0 - 15
route/tool/func_mark.py

@@ -110,24 +110,10 @@ class class_do_render:
         rep_data = curs.fetchall()
         rep_data = rep_data[0][0] if rep_data else 'namumark'
 
-        curs.execute(db_change('select html, plus, plus_t from html_filter where kind = "inter_wiki"'))
-        inter_wiki_data = curs.fetchall()
-        wiki_set_data = {
-            "inter_wiki" : {}
-        }
-        for i in inter_wiki_data:
-            wiki_set_data['inter_wiki'][i[0]] = {
-                "logo" : i[2],
-                "link" : i[1]
-            }
-
-        wiki_set_data = json.dumps(wiki_set_data, ensure_ascii = False)
-
         if data_type != 'backlink':
             if rep_data == 'namumark':
                 data_in = (data_in + '_') if data_in else ''
                 data_end = [
-                    '<pre style="display: none;" id="' + data_in + 'render_content_set">' + html.escape(wiki_set_data) + '</pre>' + \
                     '<pre style="display: none;" id="' + data_in + 'render_content_load">' + html.escape(doc_data) + '</pre>' + \
                     '<div class="render_content" id="' + data_in + 'render_content"></div>', 
                     '''
@@ -143,7 +129,6 @@ class class_do_render:
             elif rep_data == 'markdown':
                 data_in = (data_in + '_') if data_in else ''
                 data_end = [
-                    '<pre style="display: none;" id="' + data_in + 'render_content_set">' + html.escape(wiki_set_data) + '</pre>' + \
                     '<pre style="display: none;" id="' + data_in + 'render_content_load">' + html.escape(doc_data) + '</pre>' + \
                     '<div class="render_content" id="' + data_in + 'render_content"></div>', 
                     '''

+ 20 - 10
route/tool/func_tool.py

@@ -32,16 +32,26 @@ def ip_check(d_type = 0):
     ip = ''
     if d_type == 0 and (flask.session and 'id' in flask.session):
         ip = flask.session['id']
-    else:
-        ip_list = [
-            flask.request.environ.get('HTTP_X_REAL_IP', '::1'),
-            flask.request.environ.get('HTTP_X_FORWARDED_FOR', '::1'),
-            flask.request.environ.get('REMOTE_ADDR', '::1')
-        ]
-        for ip in ip_list:
-            if not (ip == '::1' or ip == '127.0.0.1'):
-                ip = ip[0] if type(ip) == type([]) else ip.split(',')[0]
-                
+    else:    
+        for for_a in range(5):
+            if for_a == 0:
+                ip = flask.request.environ.get('HTTP_X_REAL_IP', '::1')
+            elif for_a == 1:
+                ip = flask.request.environ.get('HTTP_CLIENT_IP', '::1')
+            elif for_a == 2:
+                ip = flask.request.environ.get('HTTP_X_FORWARDED_FOR', '::1')
+            elif for_a == 3:
+                ip = flask.request.environ.get('HTTP_CF_CONNECTING_IP', '::1')
+            else:
+                ip = flask.request.environ.get('REMOTE_ADDR', '::1')
+            
+            if type(ip) == type([]):
+                ip = ip[0]
+            else:
+                ip = ip.split(',')
+                ip = ip[0]
+
+            if ip != '::1':
                 break
 
     return ip

+ 132 - 108
route/topic.py

@@ -1,121 +1,145 @@
 from .tool.func import *
 
-def topic_2(conn, topic_num):
-    curs = conn.cursor()
-    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:
-        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]
+def topic(topic_num = 0):
+    with get_db_connect() as conn:
+        curs = conn.cursor()
+        topic_num = str(topic_num)
+
+        if flask.request.method == 'POST':
+            name = flask.request.form.get('topic', 'Test')
+            sub = flask.request.form.get('title', 'Test')
+            
+            if topic_num == '0':
+                curs.execute(db_change("select code from topic order by code + 0 desc limit 1"))
+                t_data = curs.fetchall()
+                topic_num = str(int(t_data[0][0]) + 1) if t_data else '1'
         else:
-            return redirect('/')
+            if topic_num == '0':
+                name = load_lang('make_new_topic')
+                sub = load_lang('make_new_topic')
+            else:
+                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('/')
 
-    ban = acl_check(name, 'topic', topic_num)
+        ban = acl_check(name, 'topic', topic_num)
 
-    if flask.request.method == 'POST':
-        if flask.request.form.get('content', 'Test') == '':
-            return redirect('/thread/' + topic_num)
+        if flask.request.method == 'POST':
+            if flask.request.form.get('content', 'Test') == '':
+                return redirect('/thread/' + topic_num)
 
-        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)
-
-        ip = ip_check()
-        today = get_time()
-
-        if ban == 1:
-            return re_error('/ban')
-
-        curs.execute(db_change("select id from topic where code = ? order by id + 0 desc limit 1"), [topic_num])
-        old_num = curs.fetchall()
-        num = str((int(old_num[0][0]) + 1) if old_num else 1)
-
-        match = re.search(r'^user:([^/]+)', name)
-        if match:
-            match = match.group(1)
-            y_check = 0
-            if ip_or_user(match) == 1:
-                curs.execute(db_change("select ip from history where ip = ? limit 1"), [match])
-                u_data = curs.fetchall()
-                if u_data:
-                    y_check = 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)
+
+            ip = ip_check()
+            today = get_time()
+
+            if ban == 1:
+                return re_error('/ban')
+
+            curs.execute(db_change("select id from topic where code = ? order by id + 0 desc limit 1"), [topic_num])
+            old_num = curs.fetchall()
+            num = str((int(old_num[0][0]) + 1) if old_num else 1)
+
+            match = re.search(r'^user:([^/]+)', name)
+            if match:
+                match = match.group(1)
+                y_check = 0
+                if ip_or_user(match) == 1:
+                    curs.execute(db_change("select ip from history where ip = ? limit 1"), [match])
+                    u_data = curs.fetchall()
+                    if u_data:
+                        y_check = 1
+                    else:
+                        curs.execute(db_change("select ip from topic where ip = ? limit 1"), [match])
+                        u_data = curs.fetchall()
+                        if u_data:
+                            y_check = 1
                 else:
-                    curs.execute(db_change("select ip from topic where ip = ? limit 1"), [match])
+                    curs.execute(db_change("select id from user_set where id = ?"), [match])
                     u_data = curs.fetchall()
                     if u_data:
                         y_check = 1
-            else:
-                curs.execute(db_change("select id from user_set where id = ?"), [match])
-                u_data = curs.fetchall()
-                if u_data:
-                    y_check = 1
-
-            if y_check == 1:
-                add_alarm(match, ip + ' | <a href="/thread/' + topic_num + '#' + num + '">' + name + ' | ' + sub + ' | #' + num + '</a>')
-
-        cate_re = re.compile(r'\[\[((?:분류|category):(?:(?:(?!\]\]).)*))\]\]', re.I)
-        data = cate_re.sub('[br]', flask.request.form.get('content', 'Test').replace('\r', ''))
-
-        for rd_data in re.findall(r"(?: |\n|^)(#(?:[0-9]+))(?: |\n|$)", 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:
-                add_alarm(ip_data[0][0], ip + ' | <a href="/thread/' + topic_num + '#' + num + '">' + name + ' | ' + sub + ' | #' + num + '</a>')
-
-        for rd_data in re.findall(r"(?: |\n|^)@((?:[^ ]+))(?: |\n|$)", data):
-            curs.execute(db_change("select ip from history where ip = ? limit 1"), [rd_data])
-            ip_data = curs.fetchall()
-            if not ip_data:
-                curs.execute(db_change("select ip from topic where ip = ? limit 1"), [rd_data])
+
+                if y_check == 1:
+                    add_alarm(match, ip + ' | <a href="/thread/' + topic_num + '#' + num + '">' + name + ' | ' + sub + ' | #' + num + '</a>')
+
+            cate_re = re.compile(r'\[\[((?:분류|category):(?:(?:(?!\]\]).)*))\]\]', re.I)
+            data = cate_re.sub('[br]', flask.request.form.get('content', 'Test').replace('\r', ''))
+
+            for rd_data in re.findall(r"(?: |\n|^)(#(?:[0-9]+))(?: |\n|$)", 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:
+                    add_alarm(ip_data[0][0], ip + ' | <a href="/thread/' + topic_num + '#' + num + '">' + name + ' | ' + sub + ' | #' + num + '</a>')
 
-            if ip_data and ip_or_user(ip_data[0][0]) == 0:
-                add_alarm(ip_data[0][0], ip + ' | <a href="/thread/' + topic_num + '#' + num + '">' + name + ' | ' + sub + ' | #' + num + '</a>')
-
-        data = re.sub(r"( |\n|^)(#(?:[0-9]+))( |\n|$)", '\g<1><topic_a>\g<2></topic_a>\g<3>', data)
-        data = re.sub(r"( |\n|^)(@(?:[^ ]+))( |\n|$)", '\g<1><topic_call>\g<2></topic_call>\g<3>', data)
-
-        rd_plus(topic_num, today, name, sub)
-        curs.execute(db_change("insert into topic (id, data, date, ip, code) values (?, ?, ?, ?, ?)"), [
-            num,
-            data,
-            today,
-            ip,
-            topic_num
-        ])
-        conn.commit()
-
-        return redirect('/thread/' + topic_num + '#' + num)
-    else:
-        display = 'display: none;' if ban == 1 else ''
-        return easy_minify(flask.render_template(skin_check(),
-            imp = [name, wiki_set(), wiki_custom(), wiki_css(['(' + load_lang('discussion') + ')', 0])],
-            data = '''
-                <h2 id="topic_top_title">''' + html.escape(sub) + '''</h2>
-                <div id="top_topic"></div>
-                <div id="main_topic"></div>
-                <div id="plus_topic"></div>
-                <script>new_topic_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 id="textarea_edit_view" class="topic_content" placeholder="''' + load_lang('content') + '''" name="content"></textarea>
+            for rd_data in re.findall(r"(?: |\n|^)@((?:[^ ]+))(?: |\n|$)", data):
+                curs.execute(db_change("select ip from history where ip = ? limit 1"), [rd_data])
+                ip_data = curs.fetchall()
+                if not ip_data:
+                    curs.execute(db_change("select ip from topic where ip = ? limit 1"), [rd_data])
+                    ip_data = curs.fetchall()
+
+                if ip_data and ip_or_user(ip_data[0][0]) == 0:
+                    add_alarm(ip_data[0][0], ip + ' | <a href="/thread/' + topic_num + '#' + num + '">' + name + ' | ' + sub + ' | #' + num + '</a>')
+
+            data = re.sub(r"( |\n|^)(#(?:[0-9]+))( |\n|$)", '\g<1><topic_a>\g<2></topic_a>\g<3>', data)
+            data = re.sub(r"( |\n|^)(@(?:[^ ]+))( |\n|$)", '\g<1><topic_call>\g<2></topic_call>\g<3>', data)
+
+            rd_plus(topic_num, today, name, sub)
+            curs.execute(db_change("insert into topic (id, data, date, ip, code) values (?, ?, ?, ?, ?)"), [
+                num,
+                data,
+                today,
+                ip,
+                topic_num
+            ])
+            conn.commit()
+
+            return redirect('/thread/' + topic_num + '#' + num)
+        else:
+            display = 'display: none;' if ban == 1 else ''
+            data_input_topic_name = ''
+            if topic_num == '0':
+                data_input_topic_name = '' + \
+                    '<input placeholder="' + load_lang('discussion_name') + '" name="title">' + \
+                    '<hr class="main_hr">' + \
+                    '<input placeholder="' + load_lang('document_name') + '" name="topic">' + \
+                    '<hr class="main_hr">' + \
+                ''
+                
+            curs.execute(db_change('select data from other where name = "topic_text"'))
+            sql_d = curs.fetchall()
+            topic_text = html.escape(sql_d[0][0]) if sql_d and sql_d[0][0] != '' else load_lang('content')
+
+            return easy_minify(flask.render_template(skin_check(),
+                imp = [name, wiki_set(), wiki_custom(), wiki_css(['(' + load_lang('discussion') + ')', 0])],
+                data = '''
+                    <h2 id="topic_top_title">''' + html.escape(sub) + '''</h2>
+                    <div id="top_topic"></div>
+                    <div id="main_topic"></div>
+                    <div id="plus_topic"></div>
+                    <script>new_topic_load("''' + topic_num + '''");</script>
+                    <a href="/thread/''' + topic_num + '/tool">(' + load_lang('topic_tool') + ''')</a>
+                    <hr class="main_hr">
+                    <form style="''' + display + '''" method="post">
+                        ''' + data_input_topic_name + '''
+                        <textarea id="textarea_edit_view" class="topic_content" placeholder="''' + topic_text + '''" name="content"></textarea>
+                        <hr class="main_hr">
+                        ''' + captcha_get() + (ip_warning() if display == '' else '') + '''
+                        <input style="display: none;" name="topic" value="''' + name + '''">
+                        <input style="display: none;" name="title" value="''' + sub + '''">
+                        <button id="save" type="submit">''' + load_lang('send') + '''</button>
+                        <button id="preview" type="button" onclick="load_preview(\'\')">''' + load_lang('preview') + '''</button>
+                    </form>
                     <hr class="main_hr">
-                    ''' + captcha_get() + (ip_warning() if display == '' else '') + '''
-                    <input style="display: none;" name="topic" value="''' + name + '''">
-                    <input style="display: none;" name="title" value="''' + sub + '''">
-                    <button id="save" type="submit">''' + load_lang('send') + '''</button>
-                    <button id="preview" type="button" onclick="load_preview(\'\')">''' + load_lang('preview') + '''</button>
-                </form>
-                <hr class="main_hr">
-                <div id="see_preview"></div>
-            ''',
-            menu = [['topic/' + url_pas(name), load_lang('list')]]
-        ))
+                    <div id="see_preview"></div>
+                ''',
+                menu = [['topic/' + url_pas(name), load_lang('list')]]
+            ))

+ 47 - 68
route/topic_close_list.py

@@ -1,78 +1,57 @@
 from .tool.func import *
 
-def topic_close_list_2(conn, name):
-    curs = conn.cursor()
+def topic_close_list(name = 'Test'):
+    with get_db_connect() as conn:
+        curs = conn.cursor()
 
-    div = ''
-    tool = flask.request.args.get('tool', '')
+        div = ''
+        tool = flask.request.args.get('tool', '')
 
-    plus = ''
-    menu = [['topic/' + url_pas(name), load_lang('return')]]
+        plus = ''
+        menu = [['topic/' + url_pas(name), load_lang('return')]]
 
-    if tool == 'close':
-        curs.execute(db_change("select code, sub from rd where title = ? and stop = 'O' order by sub asc"), [name])
+        if tool == 'close':
+            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 code, sub from rd where title = ? and agree = 'O' order by sub asc"), [name])
+            sub = load_lang('closed_discussion')
+        elif tool == 'agree':
+            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', None) == 1:
-            display = 'display: none;'
-        else:
-            display = ''
-
-        curs.execute(db_change("select code from topic order by code + 0 desc limit 1"))
-        t_data = curs.fetchall()
-        if t_data:
-            topic_num = str(int(t_data[0][0]) + 1)
+            sub = load_lang('agreed_discussion')
         else:
-            topic_num = '1'
+            sub = load_lang('discussion_list')
+            menu = [['w/' + url_pas(name), load_lang('document')]]
 
-        plus = '''
-            <a href="/topic/''' + url_pas(name) + '?tool=close">(' + load_lang('closed_discussion') + ')</a> <a href="/topic/' + url_pas(name) + '?tool=agree">(' + load_lang('agreed_discussion') + ''')</a>
-            <hr class="main_hr">
-            <form style="''' + display + '" method="post" action="/thread/' + topic_num + '''">
-                <input placeholder="''' + load_lang('discussion_name') + '''" name="title">
+            plus = '''
+                <a href="/topic/''' + url_pas(name) + '?tool=close">(' + load_lang('closed_discussion') + ''')</a>
+                <a href="/topic/''' + url_pas(name) + '?tool=agree">(' + load_lang('agreed_discussion') + ''')</a>
                 <hr class="main_hr">
-                <textarea rows="10" id="textarea_edit_view" placeholder="''' + load_lang('content') + '''" name="content"></textarea>
-                <hr class="main_hr">
-                ''' + captcha_get() + (ip_warning() if display == '' else '') + '''
-                <input style="display: none;" name="topic" value="''' + name + '''">
-                <button type="submit">''' + load_lang('send') + '''</button>
-                <button id="preview" type="button" onclick="load_preview(\'''' + url_pas(name) + '\')">' + load_lang('preview') + '''</button>
-            </form>
-            <hr class="main_hr">
-            <div id="see_preview"></div>
-        '''
-
-        curs.execute(db_change("select code, sub from rd where title = ? and stop != 'O' order by date desc"), [name])
-
-    for data in curs.fetchall():
-        curs.execute(db_change("select id from topic where code = ? order by id + 0 desc limit 1"), [data[0]])
-        t_data = curs.fetchall()
-
-        div += '''
-            <h2><a href="/thread/''' + data[0] + '">' + data[0] + '. ' + data[1] + '''</a></h2>
-            <div id="topic_pre_''' + data[0] + '''"></div>
-            <div id="topic_back_pre_''' + data[0] + '''"></div>
-            <script>
-                new_topic_load(''' + data[0] + ', "list", "?num=1", "topic_pre_' + data[0] + '''");
-                if(''' + t_data[0][0] + ''' !== 1) {
-                    new_topic_load(''' + data[0] + ', "list", "?num=' + t_data[0][0] + '", "topic_back_pre_' + data[0] + '''");
-                }
-            </script>
-        '''
-
-    if div == '':
-        plus = re.sub(r'^<br>', '', plus)
-
-    return easy_minify(flask.render_template(skin_check(),
-        imp = [name, wiki_set(), wiki_custom(), wiki_css(['(' + sub + ')', 0])],
-        data = div + plus,
-        menu = menu
-    ))
+                <a href="/thread/0">(''' + load_lang('make_new_topic') + ''')</a>
+            '''
+
+            curs.execute(db_change("select code, sub from rd where title = ? and stop != 'O' order by date desc"), [name])
+
+        for data in curs.fetchall():
+            curs.execute(db_change("select id from topic where code = ? order by id + 0 desc limit 1"), [data[0]])
+            t_data = curs.fetchall()
+
+            div += '''
+                <h2><a href="/thread/''' + data[0] + '">' + data[0] + '. ' + data[1] + '''</a></h2>
+                <div id="topic_pre_''' + data[0] + '''"></div>
+                <div id="topic_back_pre_''' + data[0] + '''"></div>
+                <script>
+                    new_topic_load(''' + data[0] + ', "list", "/normal/1", "topic_pre_' + data[0] + '''");
+                    if(''' + t_data[0][0] + ''' !== 1) {
+                        new_topic_load(''' + data[0] + ', "list", "/normal/' + t_data[0][0] + '", "topic_back_pre_' + data[0] + '''");
+                    }
+                </script>
+            '''
+
+        if div == '':
+            plus = re.sub(r'^<br>', '', plus)
+
+        return easy_minify(flask.render_template(skin_check(),
+            imp = [name, wiki_set(), wiki_custom(), wiki_css(['(' + sub + ')', 0])],
+            data = div + plus,
+            menu = menu
+        ))

+ 37 - 23
route/user_alarm.py

@@ -1,25 +1,39 @@
 from .tool.func import *
 
-def user_alarm_2(conn):
-    curs = conn.cursor()
-
-    num = int(number_check(flask.request.args.get('num', '1')))
-    sql_num = (num * 50 - 50) if num * 50 > 0 else 0
-
-    data = '<ul class="inside_ul">'
-
-    curs.execute(db_change("select data, date from alarm where name = ? order by date desc limit ?, 50"), [ip_check(), sql_num])
-    data_list = curs.fetchall()
-    if data_list:
-        data = '<a href="/alarm/delete">(' + load_lang('delete') + ')</a><hr class=\"main_hr\">' + data
-
-        for data_one in data_list:
-            data += '<li>' + html.escape(data_one[0]) + ' (' + data_one[1] + ')</li>'
-
-    data += '</ul>' + next_fix('/alarm?num=', num, data_list)
-
-    return easy_minify(flask.render_template(skin_check(),
-        imp = [load_lang('notice'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
-        data = data,
-        menu = [['user', load_lang('return')]]
-    ))
+def user_alarm():
+    with get_db_connect() as conn:
+        curs = conn.cursor()
+    
+        num = int(number_check(flask.request.args.get('num', '1')))
+        sql_num = (num * 50 - 50) if num * 50 > 0 else 0
+    
+        data = '<ul class="inside_ul">'
+    
+        curs.execute(db_change("select data, date from alarm where name = ? order by date desc limit ?, 50"), [ip_check(), sql_num])
+        data_list = curs.fetchall()
+        if data_list:
+            data = '' + \
+                '<a href="/alarm/delete">(' + load_lang('delete') + ')</a>' + \
+                '<hr class="main_hr">' + \
+                data + \
+            ''
+    
+            for data_one in data_list:
+                data += '' + \
+                    '<li>' + \
+                        '<span class="send_content">' + html.escape(data_one[0]) + '</span> ' + \
+                        '(' + data_one[1] + ')' + \
+                    '</li>' + \
+                ''
+    
+        data += '' + \
+            '</ul>' + \
+            '<script>send_render();</script>' + \
+            next_fix('/alarm?num=', num, data_list) + \
+        ''
+    
+        return easy_minify(flask.render_template(skin_check(),
+            imp = [load_lang('notice'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
+            data = data,
+            menu = [['user', load_lang('return')]]
+        ))

+ 7 - 6
route/user_alarm_del.py

@@ -1,9 +1,10 @@
 from .tool.func import *
 
-def user_alarm_del_2(conn):
-    curs = conn.cursor()
+def user_alarm_del():
+    with get_db_connect() as conn:
+        curs = conn.cursor()
+    
+        curs.execute(db_change("delete from alarm where name = ?"), [ip_check()])
+        conn.commit()
 
-    curs.execute(db_change("delete from alarm where name = ?"), [ip_check()])
-    conn.commit()
-
-    return redirect('/alarm')
+        return redirect('/alarm')

+ 1 - 1
route/user_info.py

@@ -33,7 +33,7 @@ def user_info_2(conn, name):
                 <li><a href="/login">''' + load_lang('login') + '''</a></li>
                 <li><a href="/register">''' + load_lang('register') + '''</a></li>
                 <li><a href="/change">''' + load_lang('user_setting') + '''</a></li>
-                <li><a href="/pass_find">''' + load_lang('password_search') + '''</a></li>
+                <li><a href="/login/find">''' + load_lang('password_search') + '''</a></li>
             '''
             
         tool_menu += '<li><a href="/change/head">' + load_lang('user_head') + '</a></li>'

+ 8 - 2
route/user_setting.py

@@ -44,7 +44,11 @@ def user_setting_2(conn, server_set_var):
         else:
             curs.execute(db_change('select data from user_set where name = "email" and id = ?'), [ip])
             data = curs.fetchall()
-            email = data[0][0] if data else '-'
+            email = data[0][0] if data and data[0][0] != '' else '-'
+            
+            curs.execute(db_change('select data from user_set where name = "random_key" and id = ?'), [ip])
+            data = curs.fetchall()
+            ramdom_key = data[0][0] if data and data[0][0] != '' else '-'
 
             curs.execute(db_change('select data from user_set where name = "skin" and id = ?'), [ip])
             data = curs.fetchall()
@@ -87,7 +91,9 @@ def user_setting_2(conn, server_set_var):
                         <hr class="main_hr">
                         <a href="/change/pw">(''' + load_lang('password_change') + ''')</a>
                         <hr class="main_hr">
-                        <span>''' + load_lang('email') + ''' : ''' + email + '''</span> <a href="/change/email">(''' + load_lang('email_change') + ''')</a>
+                        <span>''' + load_lang('email') + ''' : ''' + email + '''</span> <a href="/change/email">(''' + load_lang('email_change') + ''')</a> <a href="/change/email/delete">(''' + load_lang('email_delete') + ''')</a>
+                        <hr class="main_hr">
+                        <span>''' + load_lang('password_instead_key') + ''' : ''' + ramdom_key + ''' <a href="/change/key">(''' + load_lang('key_change') + ''')</a> <a href="/change/key/delete">(''' + load_lang('key_delete') + ''')</a></span>
                         <h2>''' + load_lang('main') + '''</h2>
                         <span>''' + load_lang('skin') + '''</span>
                         <hr class="main_hr">

+ 4 - 0
route/user_setting_email.py

@@ -3,6 +3,10 @@ from .tool.func import *
 def user_setting_email_2(conn):
     curs = conn.cursor()
     
+    ip = ip_check()
+    if ip_or_user(ip) != 0:
+        return redirect('/login')
+    
     if flask.request.method == 'POST':
         # c_key 같은 이름 대신 한 기능에 고유 명칭 부여 필요
         re_set_list = ['c_key']

+ 4 - 0
route/user_setting_email_check.py

@@ -2,6 +2,10 @@ from .tool.func import *
 
 def user_setting_email_check_2(conn):
     curs = conn.cursor()
+    
+    ip = ip_check()
+    if ip_or_user(ip) != 0:
+        return redirect('/login')
 
     re_set_list = ['c_key', 'c_email']
     if  not 'c_key' in flask.session or \

+ 12 - 0
route/user_setting_email_delete.py

@@ -0,0 +1,12 @@
+from .tool.func import *
+
+def user_setting_email_delete():
+    with get_db_connect() as conn:
+        curs = conn.cursor()
+
+        ip = ip_check()
+        if ip_or_user(ip) == 0:
+            curs.execute(db_change("delete from user_set where name = 'email' and id = ?"), [ip])
+            conn.commit()
+    
+        return redirect('/change')

+ 0 - 0
route/user_custom_head_view.py → route/user_setting_head.py


+ 19 - 0
route/user_setting_key.py

@@ -0,0 +1,19 @@
+from .tool.func import *
+
+def user_setting_key():
+    with get_db_connect() as conn:
+        curs = conn.cursor()
+
+        ip = ip_check()
+        if ip_or_user(ip) == 0:
+            while 1:
+                key = load_random_key()
+                curs.execute(db_change('select data from user_set where name = "random_key" and data = ?'), [key])
+                if not curs.fetchall():
+                    break
+
+            curs.execute(db_change("delete from user_set where name = 'random_key' and id = ?"), [ip])
+            curs.execute(db_change("insert into user_set (name, id, data) values ('random_key', ?, ?)"), [ip, key])
+            conn.commit()
+
+        return redirect('/change')

+ 12 - 0
route/user_setting_key_delete.py

@@ -0,0 +1,12 @@
+from .tool.func import *
+
+def user_setting_key_delete():
+    with get_db_connect() as conn:
+        curs = conn.cursor()
+
+        ip = ip_check()
+        if ip_or_user(ip) == 0:
+            curs.execute(db_change("delete from user_set where name = 'random_key' and id = ?"), [ip])
+            conn.commit()
+    
+        return redirect('/change')

+ 6 - 7
route/user_watch_list.py

@@ -4,7 +4,7 @@ def user_watch_list_2(conn, tool):
     curs = conn.cursor()
 
     if tool == 'watch_list':
-        div = load_lang("msg_whatchlist_lmt") + ' : 10 <hr class=\"main_hr\">'
+        div = load_lang("msg_whatchlist_lmt") + ' : 10 <hr class="main_hr">'
     else:
         div = ''
 
@@ -13,10 +13,6 @@ def user_watch_list_2(conn, tool):
     if ip_or_user(ip) != 0:
         return redirect('/login')
 
-
-    curs.execute(db_change("delete from scan where user = ? and title = ''"), [ip])
-    conn.commit()
-
     if tool == 'watch_list':
         curs.execute(db_change("select title from scan where type = '' and user = ?"), [ip])
 
@@ -40,14 +36,17 @@ def user_watch_list_2(conn, tool):
 
         div += '' + \
             '<li>' + \
-                '<a href="/w/' + url_pas(data_list[0]) + '">' + data_list[0] + '</a> ' + \
+                '<a href="/w/' + url_pas(data_list[0]) + '">' + html.escape(data_list[0]) + '</a> ' + \
                 plus + \
                 '<a href="/' + ('star_doc' if tool == 'star_doc' else 'watch_list') + '/' + url_pas(data_list[0]) + '">(' + load_lang('delete') + ')</a>' + \
             '</li>' + \
         ''
 
     if data:
-        div = '<ul class="inside_ul">' + div + '</ul><hr class=\"main_hr\">'
+        div = '' + \
+            '<ul class="inside_ul">' + div + '</ul>' + \
+            '<hr class="main_hr">' + \
+        ''
 
     div += '<a href="/manager/' + ('13' if tool == 'watch_list' else '16') + '">(' + load_lang('add') + ')</a>'
 

+ 2 - 0
route/user_watch_list_name.py

@@ -2,6 +2,8 @@ from .tool.func import *
 
 def user_watch_list_name_2(conn, tool, name):
     curs = conn.cursor()
+    
+    print(tool, name)
 
     ip = ip_check()
     if ip_or_user(ip) != 0:

+ 2 - 1
route/view_read.py

@@ -3,7 +3,7 @@ from .tool.func import *
 def view_read_2(conn, name, doc_rev, doc_from):
     curs = conn.cursor()
 
-    sub = ''
+    sub = 0
     menu = []
     
     user_doc = ''
@@ -14,6 +14,7 @@ def view_read_2(conn, name, doc_rev, doc_from):
     
     name_doc_pass = doc_from
     uppage = re.sub(r"/([^/]+)$", '', name)
+    uppage = 0 if uppage == name else uppage
     num = str(doc_rev)        
 
     curs.execute(db_change("select sub from rd where title = ? and not stop = 'O' order by date desc"), [name])

+ 2 - 2
version.json

@@ -1,7 +1,7 @@
 {
     "beta" : {
-        "r_ver" : "v3.5.0-stable (stable1) (beta12) (dev20)",
+        "r_ver" : "v3.4.3 (stable3) (beta21) (dev38)",
         "c_ver" : "3500101",
         "s_ver" : "3500110"
     }
-}
+}

+ 5 - 6
views/main_css/js/load_something.js

@@ -69,19 +69,18 @@ function do_twofa_check(init = 0) {
 }
 
 function send_render(i = 0) {
-    var get_class = document.getElementsByClassName('send_content')[i];
+    let get_class = document.getElementsByClassName('send_content')[i];
     if(get_class) {
         send_render(i + 1);
         
-        var data = get_class.innerHTML;
-        
+        let data = get_class.innerHTML;
         if(data === '&lt;br&gt;') {
             document.getElementsByClassName('send_content')[i].innerHTML = '<br>';
         } else {
             data = data.replace(/javascript:/i, '');
-            
-            data = data.replace(/&lt;a&gt;((?:(?!&lt;\/a&gt;).)+)&lt;\/a&gt;/g, function(x, x_1) {
-                return '<a href="/w/' + encodeURIComponent(x_1) + '">' + x_1 + '</a>';
+            data = data.replace(/&lt;a(?:(?:(?!&gt;).)*)&gt;((?:(?!&lt;\/a&gt;).)+)&lt;\/a&gt;/g, function(x, x_1) {
+                x_1_org = x_1.replace('&lt;', '<').replace('&gt;', '>');
+                return '<a href="/w/' + encodeURIComponent(x_1_org) + '">' + x_1 + '</a>';
             });
             
             document.getElementsByClassName('send_content')[i].innerHTML = data;

+ 169 - 81
views/main_css/js/render_onmark.js

@@ -140,8 +140,13 @@ function do_onmark_text_render(data) {
     return data;
 }
 
-function do_onmark_heading_render(data, data_js, name_doc, name_include) {
-    var heading_re = /\n(={1,6})(#)? ?([^=]+) ?#?={1,6}\n/;
+function do_onmark_heading_render(
+    data, 
+    data_js, 
+    name_doc, 
+    name_include
+) {
+    var heading_re = /\n(={1,6})(#)? ?([^\n]+) ?#?={1,6}\n/;
     var heading_level_all = [0, 0, 0, 0, 0, 0];
     var toc_data = '';
     var toc_n = 0;
@@ -179,23 +184,30 @@ function do_onmark_heading_render(data, data_js, name_doc, name_include) {
         }
         
         var heading_level_string_no_end = heading_level_string.replace(/\.$/, '');
-        var heading_data_text = heading_data[3].replace(/ #$/, '');
+        
+        var heading_data_text = heading_data[3].replace(/=+$/, '');
+        heading_data_text = heading_data_text.replace(/#$/, '');
         heading_data_text = heading_data_text.replace(/ $/, '');
         
         toc_data += '' +
             '<span style="margin-left: ' + String((heading_level_string.match(/\./g).length - 1) * 10) + 'px;">' +
                 '<a href="#s-' + heading_level_string_no_end + '">' + 
                     heading_level_string + ' ' +
-                '</a>' + heading_data_text +
+                '</a> ' + 
+                '<span id="toc_text_' + heading_level_string_no_end + '"></span>' +
             '</span>' +
             '\n' +
         ''
+        data_js += 'document.getElementById("toc_text_' + heading_level_string_no_end + '").innerHTML = document.getElementById("heading_text_' + heading_level_string_no_end + '").innerText;\n';
+        
         data = data.replace(heading_re, 
             '\n' +
             (toc_n === 1 ? '' : '</div>') +
             '<h' + heading_level + ' class="render_heading_text">' + 
                 '<a href="#toc" id="s-' + heading_level_string_no_end + '">' + heading_level_string + '</a> ' + 
-                heading_data_text + ' ' +
+                '<span id="heading_text_' + heading_level_string_no_end + '">' +
+                    heading_data_text + 
+                '</span> ' +
                 '<a id="edit_load_' + String(toc_n) + '" ' +
                     'style="font-size: 70%;"' +
                     'href="/edit/' + do_url_change(name_doc) + '/doc_section/' + String(toc_n) + '">✎</a> ' +
@@ -227,7 +239,7 @@ function do_onmark_heading_render(data, data_js, name_doc, name_include) {
     
     data = data.replace(toc_re, toc_data);
     
-    return data;
+    return [data, data_js];
 }
 
 function do_onmark_link_render(data, data_js, name_doc, name_include, data_nowiki, data_wiki_set) {
@@ -423,16 +435,19 @@ function do_onmark_link_render(data, data_js, name_doc, name_include, data_nowik
                     link_real = link_real.replace(/^\.\.\//, '');
                     link_real = name_doc.replace(/\/[^/]+$/, '') + (link_real !== '' ? '/' + link_real : '');
                 }
+                
+                var link_data_var = do_link_change(link_real, data_nowiki, 0);
+                var link_main = link_data_var[0];
+                var link_sub = link_data_var[1];
+                
+                let link_id = "real_normal_link"
 
                 var i = 0;
                 while(i < 2) {
                     if(i === 0) {
-                        var link_data_var = do_link_change(link_real, data_nowiki, 0);
-                        var link_main = link_data_var[0];
-                        var link_sub = link_data_var[1];
-
                         var var_link_type = 'href';
                         if(link_main === '') {
+                            link_id = "in_doc_link"
                             var var_link_data = link_sub;
                         } else {
                             var var_link_data = '/w/' + do_url_change(link_main) + link_sub;
@@ -452,7 +467,7 @@ function do_onmark_link_render(data, data_js, name_doc, name_include, data_nowik
                 }
 
                 return  '<a class="' + name_include + 'link_finder" ' +
-                            'id="real_normal_link"' +
+                            'id="' + link_id + '"' +
                             'name="' + name_include + 'set_link_' + num_link_str + '" ' +
                             'title="" ' +
                             'href="">' + link_out + '</a>';
@@ -544,7 +559,7 @@ function do_onmark_footnote_render(data, name_include) {
 function do_onmark_macro_render(data, data_js) {
     data = data.replace(/\[([^[\](]+)\(((?:(?!\)\]).)+)\)\]/g, function(x, x_1, x_2) {
         x_1 = x_1.toLowerCase();
-        if(x_1 === 'youtube' || x_1 === 'kakaotv' || x_1 === 'nicovideo' || x_1 === 'navertv') {
+        if(x_1 === 'youtube' || x_1 === 'kakaotv' || x_1 === 'nicovideo' || x_1 === 'navertv' || x_1 === 'vimeo') {
             var video_code = x_2.match(/^([^,]+)/);
             video_code = video_code ? video_code[1] : '';
             
@@ -569,9 +584,11 @@ function do_onmark_macro_render(data, data_js) {
                 var video_src = 'https://tv.kakao.com/embed/player/cliplink/' + video_code +'?service=kakao_tv'
             } else if(x_1 === 'nicovideo') {
                 var video_src = 'https://embed.nicovideo.jp/watch/' + video_code
-            } else {
+            } else if(x_1 === 'navertv') {
                 var video_src = 'https://tv.naver.com/embed/' + video_code
-            }
+            } else {
+		var video_src = 'https://player.vimeo.com/video/' + video_code
+	    }
             
             return '<iframe style="width: ' + video_width + '; height: ' + video_height + ';" src="' + video_src + '" frameborder="0" allowfullscreen></iframe>';
         } else if(x_1 === 'anchor') {
@@ -598,7 +615,7 @@ function do_onmark_macro_render(data, data_js) {
             
             var date_end = Math.floor((date_now - date_old) / (24 * 60 * 60 * 1000));
             
-            return date_end > 0 ? '+' + date_end : '-' + date_end;
+            return (date_end > 0 ? '+' : '') + date_end;
         } else if(x_1 === 'age') {
             var date_old = new Date(x_2);
             var date_now = new Date(do_return_date());
@@ -616,7 +633,7 @@ function do_onmark_macro_render(data, data_js) {
     var pagecount_n = 0;
     data = data.replace(/\[([^[*()\]]+)\]/g, function(x, x_1) {
         x_1 = x_1.toLowerCase();
-        if(x_1 === 'date') {
+        if(x_1 === 'date' || x_1 === 'datetime') {
             return do_return_date();
         } else if(x_1 === 'clearfix') {
             return '<div style="clear:both"></div>';
@@ -1260,7 +1277,9 @@ function do_onmark_redirect_render(data, data_js, name_doc) {
         }
         
         return [
-            data.replace(redirect_re, '/w/' + do_url_change(link_main) + '/doc_from/' + do_url_change(name_doc) + link_sub), 
+            '/w/' + do_url_change(link_main) + 
+            '/doc_from/' + do_url_change(name_doc) + 
+            link_sub,
             data_js, 
             1
         ];
@@ -1290,7 +1309,6 @@ function do_onmark_render(
         var data = '\n' + 
             document.getElementById(name_id + '_load').innerHTML.replace(/\r/g, '') + 
         '\n';
-        data_wiki_set = JSON.parse(document.getElementById(name_id + '_set').innerHTML);
     } else if(test_mode === 'manual') { 
         var data = '\n' + 
             doc_data.replace(/\r/g, '') + 
@@ -1304,71 +1322,141 @@ function do_onmark_render(
     var data_backlink = [];
     var data_nowiki = {};
 
-    var data_var = do_onmark_redirect_render(data, data_js, name_doc, name_include);
-    data = data_var[0];
-    data_js = data_var[1];
-    var passing = data_var[2];
-
-    if(passing === 0) {
-        data = do_onmark_remark_render(data);
+    let xhr = new XMLHttpRequest();
+    xhr.open("GET", "/api/setting/inter_wiki");
+    xhr.send();
+
+    xhr.onreadystatechange = function() {
+        if(this.readyState === 4 && this.status === 200) {
+            data_wiki_set = JSON.parse(this.responseText);
+            let data_wiki_set_inter_wiki = { "inter_wiki" : {}};
+            if(data_wiki_set["inter_wiki"]) {
+                for(let i = 0; i < data_wiki_set["inter_wiki"].length; i++) {
+                    data_wiki_set_inter_wiki["inter_wiki"][
+                        data_wiki_set["inter_wiki"][i][0]
+                    ] = {
+                        "link" : data_wiki_set["inter_wiki"][i][1],
+                        "logo" : data_wiki_set["inter_wiki"][i][2]
+                    }
+                }
+            }
+            
+            let data_var = do_onmark_redirect_render(
+                data, 
+                data_js, 
+                name_doc, 
+                name_include
+            );
+            data = data_var[0];
+            data_js = data_var[1];
+            let passing = data_var[2];
+            
+            if(passing === 1) {
+                if(test_mode === 'normal') {
+                    document.getElementById(name_id).innerHTML = data + '<script>' + data_js + '</script>';
+                    eval(data_js);
+                } else if(test_mode === 'manual') {
+                    return [data, data_js];
+                } else {
+                	console.log([data, data_js]);
+                }
+                
+                return 0;
+            }
+            
+            data = do_onmark_remark_render(data);
+            
+            data_var = do_onmark_nowiki_before_render(
+                data, 
+                data_js, 
+                name_include, 
+                data_nowiki
+            );
+            data = data_var[0];
+            data_js = data_var[1];
+            data_nowiki = data_var[2];
+            
+            data_var = do_onmark_math_render(
+                data, 
+                data_js, 
+                name_include, 
+                data_nowiki
+            );
+            data = data_var[0];
+            data_js = data_var[1];
         
-        data_var = do_onmark_nowiki_before_render(data, data_js, name_include, data_nowiki);
-        data = data_var[0];
-        data_js = data_var[1];
-        data_nowiki = data_var[2];
+            data_var = do_onmark_include_render(
+                data, 
+                data_js, 
+                name_include, 
+                data_nowiki
+            );
+            data = data_var[0];
+            data_js = data_var[1];
         
-        data_var = do_onmark_math_render(data, data_js, name_include, data_nowiki);
-        data = data_var[0];
-        data_js = data_var[1];
-
-        data_var = do_onmark_include_render(data, data_js, name_include, data_nowiki);
-        data = data_var[0];
-        data_js = data_var[1];
-
-        data_var = do_onmark_middle_render(data, data_js, name_include, data_nowiki, name_doc);
-        data = data_var[0];
-        data_js = data_var[1];
-        data_nowiki = data_var[2];
-
-        data = do_onmark_text_render(data);
-        data = do_onmark_heading_render(data, data_js, name_doc, name_include);
-        data = do_onmark_table_render(data);
-
-        data_var = do_onmark_link_render(
-            data, 
-            data_js, 
-            name_doc, 
-            name_include,
-            data_nowiki,
-            data_wiki_set
-        );
-        data = data_var[0];
-        data_js = data_var[1];
-        var data_category = data_var[2];
-
-        data_var = do_onmark_macro_render(data, data_js);
-        data = data_var[0];
-        data_js = data_var[1];
+            data_var = do_onmark_middle_render(
+                data, 
+                data_js, 
+                name_include, 
+                data_nowiki, 
+                name_doc
+            );
+            data = data_var[0];
+            data_js = data_var[1];
+            data_nowiki = data_var[2];
         
-        data = do_onmark_list_render(data);
-        data = do_onmark_hr_render(data);
-        data = do_onmark_footnote_render(data, name_include);
-        data = do_onmark_last_render(data, name_include, data_category);
-    }
-    
-    data_js += '' + 
-        'get_link_state("' + name_include + '");\n' + 
-        'get_file_state("' + name_include + '");\n' + 
-		'get_heading_name();' +
-        'render_html("' + name_include + 'nowiki_html");\n' +
-    ''
-    
-    if(test_mode === 'normal') {
-        document.getElementById(name_id).innerHTML = data + '<script>' + data_js + '</script>';
-        eval(data_js);
-    } else if(test_mode === 'manual') {
-        return [data, data_js];
-    } else {
-    	console.log([data, data_js]);
+            data = do_onmark_text_render(data);
+            data_var = do_onmark_heading_render(
+                data, 
+                data_js, 
+                name_doc, 
+                name_include
+            );
+            data = data_var[0];
+            data_js = data_var[1];
+            
+            data = do_onmark_table_render(data);
+        
+            data_var = do_onmark_link_render(
+                data, 
+                data_js, 
+                name_doc, 
+                name_include,
+                data_nowiki,
+                data_wiki_set_inter_wiki
+            );
+            data = data_var[0];
+            data_js = data_var[1];
+            var data_category = data_var[2];
+        
+            data_var = do_onmark_macro_render(data, data_js);
+            data = data_var[0];
+            data_js = data_var[1];
+            
+            data = do_onmark_list_render(data);
+            data = do_onmark_hr_render(data);
+            data = do_onmark_footnote_render(data, name_include);
+            data = do_onmark_last_render(
+                data, 
+                name_include, 
+                data_category
+            );
+            
+            data_js += '' + 
+                'get_link_state("' + name_include + '");\n' + 
+                'get_file_state("' + name_include + '");\n' + 
+        		'get_heading_name();\n' +
+                'render_html("' + name_include + 'nowiki_html");\n' +
+            ''
+            
+            if(test_mode === 'normal') {
+                document.getElementById(name_id).innerHTML = data + '<script>' + data_js + '</script>';
+                eval(data_js);
+            } else if(test_mode === 'manual') {
+                return [data, data_js];
+            } else {
+            	console.log([data, data_js]);
+            }
+        }
     }
 }

+ 6 - 5
views/main_css/js/render_wiki.js

@@ -8,10 +8,11 @@ function get_link_state(data) {
     var link_list_2 = {}
     for(var i = 0; document.getElementsByClassName(data + 'link_finder')[i]; i++) {
         var data_class = document.getElementsByClassName(data + 'link_finder')[i];
+        console.log(data_class.href)
         if(
             data_class.id !== 'out_link' && 
             data_class.id !== 'inside' && 
-            !data_class.href.match(/^#/)
+            data_class.id !== 'in_doc_link'
         ) {            
             link_list.push(data_class.title);
             
@@ -53,10 +54,10 @@ function get_link_state(data) {
 }
 
 function get_heading_name() {
-	let heading_name = document.getElementsByClassName('render_heading_text');
-	for(let i = 0; i < heading_name.length; i++) {
-		heading_name[i].id = heading_name[i].innerText.replace(/^([0-9]+\.)+ /, '').replace(/✎ ⊖$/, '');
-	}
+    let heading_name = document.getElementsByClassName('render_heading_text');
+    for(let i = 0; i < heading_name.length; i++) {
+        heading_name[i].id = heading_name[i].innerText.replace(/^([0-9]+\.)+ /, '').replace(/ ✎ ⊖$/, '');
+    }
 }
 
 function load_image_link(data) {

+ 47 - 1
views/ringo/css/main.css

@@ -3,6 +3,7 @@ body {
 }
 
 html {
+    font-family: "나눔바른고딕", "나눔고딕", "맑은고딕", "Sans-Serif";
     background: #eee;
 }
 
@@ -35,6 +36,19 @@ header#main form.not_mobile {
     display: inline-block;
 }
 
+div#menu {
+    margin-top: 10px;
+}
+
+.menu_item {
+    border: 0;
+    background-color: white;
+    
+    padding: 10px;
+    
+    display: inline-block;
+}
+
 input.search {
     height: 35px;
     
@@ -101,6 +115,30 @@ aside button {
     width: 25%;
 }
 
+aside .side_button {
+    border: 0;
+    background-color: white;
+    
+    padding: 10px;
+}
+
+aside .side_button:hover {
+    background-color: #eee;
+}
+
+#side_button_2 {
+    border-left: 0;
+    border-right: 0;
+}
+
+#side_button_3 {
+    border-right: 0;
+}
+
+#side_content {
+    margin-top: 20px;
+}
+
 section {
     width: calc(100% - (250px + 40px));
     
@@ -129,7 +167,7 @@ article.main {
     margin: auto;
 }
 
-article.main#content {
+article.main#main_data {
     padding-top: 20px;
     
     min-height: 400px;
@@ -183,4 +221,12 @@ footer {
 
 footer.only_mobile {
     margin-top: 0px;
+}
+
+#main_data input, #main_data textarea, #main_data button, #main_data select {
+    border: 1px solid #aaa;
+    
+    padding: 10px;
+    
+    background-color: white;
 }

+ 33 - 7
views/ringo/index.html

@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<html>
+<html lang="{{'language_tag'|load_lang}}">
     <head>
         <meta charset="utf-8">
         {% if imp[3][0] != 0 %}
@@ -48,7 +48,7 @@
                             <span class="iconify" data-icon="ic:baseline-person-add" data-inline="true"></span>
                         {% endif %}
                     {% else %}
-                        <span class="iconify" data-icon="ic:baseline-person" data-inline="true"></span>
+                        <span class="iconify" data-icon="ic:round-person-search" data-inline="true"></span>
                     {% endif %}
                     <span class="not_mobile">{{'tool'|load_lang}}</span>
                     <span class="iconify" data-icon="ic:baseline-arrow-drop-down" data-inline="true"></span>
@@ -77,9 +77,32 @@
             <header id="section">
                 <article class="main" id="title">
                     <h1><span class="change_space">{{imp[0]}}</span></h1>
+                    {% if menu != 0 %}
+                        <div id="menu">
+                            {% for menu_data in menu %}<!--
+                             --><div class="menu_item">
+                                    {% if menu[0] == 1 %}
+                                        {% set menu_data_2 = menu_data[0] %}
+                                    {% else %}
+                                        {% set menu_data_2 = '/' + menu_data[0] %}
+                                    {% endif %}
+                                    
+                                    {% if menu_data|length > 2 and menu_data[2] == 1 %}
+                                        {% set menu_topic = 'topic_color' %}
+                                    {% else %}
+                                        {% set menu_topic = '' %}
+                                    {% endif %}
+                                    
+                                    <a class="menu_item_link" href="{{menu_data_2}}" id="{{menu_topic}}">
+                                        {{menu_data[1]}}
+                                    </a>
+                                </div><!--
+                         -->{% endfor %}
+                        </div>
+                    {% endif %}
                 </article>
             </header>
-            <article class="main" id="content">
+            <article class="main" id="main_data">
                 {{data|safe}}
             </article>
             <footer class="not_mobile">
@@ -92,10 +115,13 @@
             </footer>
         </section>
         <aside>
-            <button class="side_button selected">A</button><!--
-         --><button class="side_button">B</button><!--
-         --><button class="side_button">C</button><!--
-         --><button class="side_button">D</button>
+            <button id="side_button_1" class="side_button selected">변경</button><!--
+         --><button id="side_button_2" class="side_button">토론</button><!--
+         --><button id="side_button_3" class="side_button">목차</button><!--
+         --><button id="side_button_4" class="side_button">각주</button>
+            <div id="side_content">
+                Loading...
+            </div>
         </aside>
         <footer class="only_mobile">
             {{imp[1][1]|safe}}

+ 3 - 3
views/tenshi/index.html

@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<html>
+<html lang="{{'language_tag'|load_lang}}">
     <head>
         <meta charset="utf-8">
         {% if imp[3][0] != 0 %}
@@ -102,10 +102,10 @@
                                         {% if imp[2][8] != '0' %}
                                             <span class="iconify" data-icon="ic:baseline-add-alert" data-inline="true"></span>
                                         {% else %}
-                                            <span class="iconify" data-icon="ic:baseline-person-add" data-inline="true"></span>
+                                            <span class="iconify" data-icon="ic:baseline-person" data-inline="true"></span>
                                         {% endif %}
                                     {% else %}
-                                        <span class="iconify" data-icon="ic:baseline-person" data-inline="true"></span>
+                                        <span class="iconify" data-icon="ic:round-person-search" data-inline="true"></span>
                                     {% endif %}
                                     <span class="not_mobile">{{imp[2][5]}}</span>
                                 </a>