ソースを参照

Merge pull request #1857 from openNAMU/beta

임시로 stable 버전 올리기
잉여개발기 (SPDV) 2 年 前
コミット
c03ab28ac9
74 ファイル変更1749 行追加1173 行削除
  1. 1 1
      .github/PULL_REQUEST_TEMPLATE.md
  2. 59 74
      app.py
  3. 8 1
      emergency_tool.py
  4. 27 0
      lang/en-US.json
  5. 29 4
      lang/ko-KR.json
  6. 1 2
      readme-en.md
  7. 1 2
      readme.md
  8. 2 0
      requirements.txt
  9. 5 3
      route/__init__.py
  10. 29 0
      route/api_bbs_w_comment.py
  11. 26 0
      route/api_bbs_w_post.py
  12. 2 1
      route/api_skin_info.py
  13. 110 15
      route/api_topic.py
  14. 1 2
      route/api_w.py
  15. 7 1
      route/bbs_main.py
  16. 2 2
      route/bbs_make.py
  17. 11 11
      route/bbs_w.py
  18. 51 58
      route/bbs_w_edit.py
  19. 184 195
      route/bbs_w_post.py
  20. 24 26
      route/bbs_w_set.py
  21. 91 54
      route/edit.py
  22. 10 12
      route/edit_delete_file.py
  23. 1 0
      route/edit_delete_multiple.py
  24. 1 1
      route/give_admin_groups.py
  25. 1 1
      route/give_delete_admin_group.py
  26. 3 4
      route/list_image_file.py
  27. 8 5
      route/list_long_page.py
  28. 2 0
      route/list_old_page.py
  29. 40 27
      route/list_user_check.py
  30. 5 15
      route/list_user_check_delete.py
  31. 19 5
      route/login_login.py
  32. 1 1
      route/login_register_submit.py
  33. 2 23
      route/main_func_easter_egg.py
  34. 1 0
      route/main_setting.py
  35. 8 5
      route/main_setting_main.py
  36. 5 1
      route/main_setting_phrase.py
  37. 5 0
      route/main_setting_skin_set.py
  38. 3 0
      route/main_sys_update.py
  39. 7 4
      route/main_tool_redirect.py
  40. 1 1
      route/recent_block.py
  41. 1 2
      route/recent_history_add.py
  42. 468 481
      route/tool/func.py
  43. 37 9
      route/tool/func_render.py
  44. 230 39
      route/tool/func_render_namumark.py
  45. 1 0
      route/tool/func_tool.py
  46. 21 57
      route/topic.py
  47. 1 0
      route/topic_list.py
  48. 1 1
      route/user_info.py
  49. 1 3
      route/user_setting.py
  50. 9 0
      route/user_setting_skin_set_main.py
  51. 4 5
      route/view_acl.py
  52. 4 4
      route/view_diff.py
  53. 1 1
      route/view_read.py
  54. 1 1
      route/vote_add.py
  55. 2 2
      route/vote_select.py
  56. BIN
      route_go/bin/main_easter_egg.amd64.bin
  57. BIN
      route_go/bin/main_easter_egg.amd64.exe
  58. BIN
      route_go/bin/main_easter_egg.arm64.bin
  59. BIN
      route_go/bin/main_easter_egg.arm64.exe
  60. 22 0
      route_go/linux_amd64.sh
  61. 37 0
      route_go/main_easter_egg.go
  62. 1 1
      version.json
  63. 4 0
      views/main_css/css/main.css
  64. 5 5
      views/main_css/css/sub/dark.css
  65. 3 0
      views/main_css/js/func/check_new_thread.ts
  66. 5 1
      views/main_css/js/func/func.js
  67. 15 0
      views/main_css/js/route/bbs_w_post.js
  68. 14 0
      views/main_css/js/route/bbs_w_post.ts
  69. 52 1
      views/main_css/js/route/editor.js
  70. 8 0
      views/ringo/css/main.css
  71. 1 1
      views/ringo/index.html
  72. 4 0
      views/tenshi/css/main.css
  73. 1 1
      views/tenshi/index.html
  74. 1 1
      views/tenshi/info.json

+ 1 - 1
.github/PULL_REQUEST_TEMPLATE.md

@@ -1,4 +1,4 @@
 <!--
-stable, beta branch로 요청을 보내지 마십시오. 개발은 dev branch에서 이루어집니다.
+stable, beta branch로 요청을 보내지 말아주세요. 개발은 dev branch에서 이루어집니다.
 Don't request merge your commit to stable, beta branch, please request to dev branch.
 -->

+ 59 - 74
app.py

@@ -1,10 +1,16 @@
 # Init
 import os
 import re
+import logging
+import shutil
 
 from route.tool.func import *
 from route import *
 
+if platform.system() == 'Linux':
+    for for_a in os.listdir(os.path.join("route_go", "bin")):
+        os.system('chmod +x ./route_go/bin/' + for_a)
+
 # Init-Version
 with open('version.json', encoding = 'utf8') as file_data:
     version_list = json.loads(file_data.read())
@@ -293,52 +299,40 @@ app.route('/extension_filter/del/<everything:name>', defaults = { 'tool' : 'del_
 app.route('/extension_filter/add', methods = ['POST', 'GET'], defaults = { 'tool' : 'plus_extension_filter' })(filter_inter_wiki_add)
 
 # Func-list
-# /list/document/old
 app.route('/list/document/old')(list_old_page)
 app.route('/list/document/old/<int:num>')(list_old_page)
 
-# /list/document/acl
 app.route('/list/document/acl')(list_acl)
 app.route('/list/document/acl/<int:arg_num>')(list_acl)
 
-# /list/document/need
 app.route('/list/document/need')(list_please)
 app.route('/list/document/need/<int:arg_num>')(list_please)
 
-# /list/document/all
 app.route('/list/document/all')(list_title_index)
 app.route('/list/document/all/<int:num>')(list_title_index)
 
-# /list/document/long
 app.route('/list/document/long')(list_long_page)
+app.route('/list/document/long/<int:arg_num>')(list_long_page)
 
-# /list/document/short
 app.route('/list/document/short', defaults = { 'tool' : 'short_page' })(list_long_page)
+app.route('/list/document/short/<int:arg_num>', defaults = { 'tool' : 'short_page' })(list_long_page)
 
-# /list/file
 app.route('/list/file')(list_image_file)
+app.route('/list/file/<int:arg_num>')(list_image_file)
 
-# /list/admin
-# /list/admin/list
 app.route('/list/admin')(list_admin)
 
-# /list/admin/auth_use
 app.route('/list/admin/auth_use', methods = ['POST', 'GET'])(list_admin_auth_use)
 app.route('/list/admin/auth_use/<arg_search>/<int:arg_num>', methods = ['POST', 'GET'])(list_admin_auth_use)
 
-# /list/user
 app.route('/list/user')(list_user)
 app.route('/list/user/<int:arg_num>')(list_user)
 
-# /list/user/check
-@app.route('/check/<name>')
-def give_user_check(name = None):
-    return give_user_check_2(name)
-    
-# /list/user/check/delete
-@app.route('/check_delete', methods = ['POST', 'GET'])
-def give_user_check_delete():
-    return give_user_check_delete_2()
+app.route('/list/user/check/<name>')(list_user_check)
+app.route('/list/user/check/<name>/<do_type>')(list_user_check)
+app.route('/list/user/check/<name>/<do_type>/<int:arg_num>')(list_user_check)
+app.route('/list/user/check/<name>/<do_type>/<int:arg_num>/<plus_name>')(list_user_check)
+app.route('/list/user/check/delete/<name>/<ip>/<time>/<do_type>', methods = ['POST', 'GET'])(list_user_check_delete)
 
 # Func-auth
 # /auth/give
@@ -354,33 +348,23 @@ app.route('/auth/give/ban_regex/<everything:name>', methods = ['POST', 'GET'], d
 app.route('/auth/give/ban_multiple', methods = ['POST', 'GET'], defaults = { 'ban_type' : 'multiple' })(give_user_ban)
 
 # /auth/list
-@app.route('/admin_group')
-def list_admin_group():
-    return list_admin_group_2()
+app.route('/admin_group')(list_admin_group_2)
 
 # /auth/list/add/<name>
-@app.route('/admin_plus/<name>', methods = ['POST', 'GET'])
-def give_admin_groups(name = None):
-    return give_admin_groups_2(name)
+app.route('/admin_plus/<name>', methods = ['POST', 'GET'])(give_admin_groups_2)
 
 # /auth/list/delete/<name>
-@app.route('/delete_admin_group/<name>', methods = ['POST', 'GET'])
-def give_delete_admin_group(name = None):
-    return give_delete_admin_group_2(name)
+app.route('/delete_admin_group/<name>', methods = ['POST', 'GET'])(give_delete_admin_group_2)
 
 app.route('/auth/give/fix/<user_name>', methods = ['POST', 'GET'])(give_user_fix)
 
-@app.route('/app_submit', methods = ['POST', 'GET'])
-def recent_app_submit():
-    return recent_app_submit_2()
+app.route('/app_submit', methods = ['POST', 'GET'])(recent_app_submit_2)
 
 # /auth/history
 # ongoing 반영 필요
-@app.route('/block_log')
-@app.route('/block_log/<regex("user"):tool>/<name>')
-@app.route('/block_log/<regex("admin"):tool>/<name>')
-def recent_block(name = 'Test', tool = 'all'):
-    return recent_block_2(name, tool)
+app.route('/block_log')(recent_block_2)
+app.route('/block_log/<regex("user"):tool>/<name>')(recent_block_2)
+app.route('/block_log/<regex("admin"):tool>/<name>')(recent_block_2)
 
 # Func-history
 app.route('/recent_change')(recent_change)
@@ -495,15 +479,9 @@ app.route('/star_doc', defaults = { 'tool' : 'star_doc' })(user_watch_list)
 app.route('/star_doc/<everything:name>', defaults = { 'tool' : 'star_doc' })(user_watch_list_name)
 
 # 개편 보류중 S
-@app.route('/change/email', methods = ['POST', 'GET'])
-def user_setting_email():
-    return user_setting_email_2()
-
+app.route('/change/email', methods = ['POST', 'GET'])(user_setting_email_2)
 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()
+app.route('/change/email/check', methods = ['POST', 'GET'])(user_setting_email_check_2)
 # 개편 보류중 E
 
 # Func-login
@@ -513,29 +491,12 @@ def user_setting_email_check():
 # register -> register/email -> regiter/email/check with reg_id
 # pass_find -> pass_find/email with find_id
 
-@app.route('/login', methods = ['POST', 'GET'])
-def login_login():
-    return login_login_2()
-
-@app.route('/login/2fa', methods = ['POST', 'GET'])
-def login_login_2fa():
-    return login_login_2fa_2()
-
-@app.route('/register', methods = ['POST', 'GET'])
-def login_register():
-    return login_register_2()
-
-@app.route('/register/email', methods = ['POST', 'GET'])
-def login_register_email():
-    return login_register_email_2()
-
-@app.route('/register/email/check', methods = ['POST', 'GET'])
-def login_register_email_check():
-    return login_register_email_check_2()
-
-@app.route('/register/submit', methods = ['POST', 'GET'])
-def login_register_submit():
-    return login_register_submit_2()
+app.route('/login', methods = ['POST', 'GET'])(login_login_2)
+app.route('/login/2fa', methods = ['POST', 'GET'])(login_login_2fa_2)
+app.route('/register', methods = ['POST', 'GET'])(login_register_2)
+app.route('/register/email', methods = ['POST', 'GET'])(login_register_email_2)
+app.route('/register/email/check', methods = ['POST', 'GET'])(login_register_email_check_2)
+app.route('/register/submit', methods = ['POST', 'GET'])(login_register_submit_2)
 
 app.route('/login/find')(login_find)
 app.route('/login/find/key', methods = ['POST', 'GET'])(login_find_key)
@@ -560,11 +521,11 @@ app.route('/bbs/make', methods = ['POST', 'GET'])(bbs_make)
 # app.route('/bbs/main/set')
 app.route('/bbs/w/<int:bbs_num>')(bbs_w)
 app.route('/bbs/set/<int:bbs_num>', methods = ['POST', 'GET'])(bbs_w_set)
-app.route('/bbs/edit/<int:bbs_num>', methods = ['POST', 'GET'])(bbs_edit)
-app.route('/bbs/edit/preview/<int:bbs_num>', methods = ['POST', 'GET'], defaults = { 'do_type' : 'preview' })(bbs_edit)
+app.route('/bbs/edit/<int:bbs_num>', methods = ['POST', 'GET'])(bbs_w_edit)
+app.route('/bbs/edit/preview/<int:bbs_num>', methods = ['POST', 'GET'], defaults = { 'do_type' : 'preview' })(bbs_w_edit)
 app.route('/bbs/w/<int:bbs_num>/<int:post_num>', methods = ['POST', 'GET'])(bbs_w_post)
-app.route('/bbs/edit/<int:bbs_num>/<int:post_num>', methods = ['POST', 'GET'])(bbs_edit)
-app.route('/bbs/edit/preview/<int:bbs_num>/<int:post_num>', methods = ['POST', 'GET'], defaults = { 'do_type' : 'preview' })(bbs_edit)
+app.route('/bbs/edit/<int:bbs_num>/<int:post_num>', methods = ['POST', 'GET'])(bbs_w_edit)
+app.route('/bbs/edit/preview/<int:bbs_num>/<int:post_num>', methods = ['POST', 'GET'], defaults = { 'do_type' : 'preview' })(bbs_w_edit)
 app.route('/bbs/w/preview/<int:bbs_num>/<int:post_num>', methods = ['POST'], defaults = { 'do_type' : 'preview' })(bbs_w_post)
 # app.route('/bbs/edit/<int:bbs_num>/<int:post_num>')
 
@@ -574,6 +535,9 @@ app.route('/api/w/<everything:name>/doc_tool/<tool>', methods = ['POST', 'GET'])
 app.route('/api/w/<everything:name>', methods = ['GET', 'POST'])(api_w)
 app.route('/api/raw/<everything:name>')(api_raw)
 
+app.route('/api/bbs/w/<sub_code>')(api_bbs_w_post)
+app.route('/api/bbs/w/comment/<sub_code>')(api_bbs_w_comment)
+
 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)
@@ -638,6 +602,28 @@ app.route('/setting/skin_set', methods = ['POST', 'GET'])(main_setting_skin_set)
 
 app.route('/easter_egg')(main_func_easter_egg)
 
+def main_easter_egg_go():
+    with get_db_connect() as conn:
+        print(platform.machine())
+        if platform.system() == 'Linux':
+            if platform.machine() in ["AMD64", "x86_64"]:
+                data = os.popen(os.path.join(".", "route_go", "bin", "main_easter_egg.amd64.bin")).read()
+            else:
+                data = os.popen(os.path.join(".", "route_go", "bin", "main_easter_egg.arm64.bin")).read()
+        else:
+            if platform.machine() in ["AMD64", "x86_64"]:
+                data = os.popen(os.path.join(".", "route_go", "bin", "main_easter_egg.amd64.exe")).read()
+            else:
+                data = os.popen(os.path.join(".", "route_go", "bin", "main_easter_egg.arm64.exe")).read()
+
+        return easy_minify(flask.render_template(skin_check(),
+            imp = ['Easter Egg', wiki_set(), wiki_custom(), wiki_css([0, 0])],
+            data = data,
+            menu = 0
+        ))
+
+app.route('/easter_egg_go')(main_easter_egg_go)
+
 # views -> view
 app.route('/view/<path:name>')(main_view)
 app.route('/views/<path:name>')(main_view)
@@ -656,6 +642,5 @@ if __name__ == "__main__":
         app,
         host = server_set['host'],
         port = int(server_set['port']),
-        threads = 1,
         clear_untrusted_proxy_headers = True
-    )
+    )

+ 8 - 1
emergency_tool.py

@@ -4,6 +4,7 @@ import os
 import platform
 import urllib
 import zipfile
+import urllib.request
 
 from route.tool.func import *
 
@@ -25,7 +26,7 @@ if data_db_load == 'Y':
     curs = conn.cursor()
 else:
     print('----')
-    print('You can use [9, 11]')
+    print('You can use [9, 11, 19]')
 
 # Main
 print('----')
@@ -49,6 +50,8 @@ print('18. Change wiki access password')
 print('19. Forced update')
 print('20. Change domain')
 print('21. Change TLS')
+print('22. Delete body top')
+print('23. Delete body bottom')
 
 print('----')
 what_i_do = input('Select : ')
@@ -249,6 +252,10 @@ elif what_i_do == '21':
 
     curs.execute(db_change('delete from other where name = "http_select"'))
     curs.execute(db_change('insert into other (name, data, coverage) values ("http_select", ?, "")'), [tls_v])
+elif what_i_do == '22':
+    curs.execute(db_change('delete from other where name = "body"'))
+elif what_i_do == '23':
+    curs.execute(db_change('delete from other where name = "bottom_body"'))
 else:
     raise ValueError(what_i_do)
 

+ 27 - 0
lang/en-US.json

@@ -240,6 +240,25 @@
         "user_added_menu" : "User added menu",
         "move_redirect_make" : "Redirect document generation (Only if possible)",
         "user_fix" : "Fix user",
+        "sub_user_name" : "Sub user name",
+        "_comment_" : "BBS",
+            "bbs" : "BBS",
+            "bbs_main" : "BBS main",
+            "bbs_name" : "BBS name",
+            "bbs_make" : "Create BBS",
+            "order" : "Order",
+            "comment_base" : "Comment based",
+            "thread_base" : "Thread based",
+            "bbs_set" : "BBS set",
+            "comment" : "Comment",
+            "reply" : "Reply",
+            "post_edit" : "Modify post",
+            "post_add" : "Add post",
+            "_comment_" : "BBS ACL",
+                "bbs_view_acl" : "BBS ACL to view posts",
+                "bbs_acl" : "BBS ACL",
+                "bbs_edit_acl" : "BBS ACL to write post",
+                "bbs_comment_acl" : "BBS ACL to write comment",
         "_comment_" : "Edit",
             "load" : "Load another document",
             "turn_off_monaco" : "Turn off monaco editor",
@@ -263,6 +282,10 @@
                 "font_size" : "Font size",
                 "image_paste" : "Paste Image by Ctrl + C and V",
                 "monaco_editor" : "Monaco editor",
+                "footnote_render" : "Footnote rendering",
+                "footnote_number" : "Footnote number output",
+                "only_number" : "Only number",
+                "footnote_real_num_view" : "Footnote original number view",
             "_comment_" : "Option",
                 "change_to_normal" : "Change to plain text.",
                 "change_to_link" : "Change to Link.",
@@ -318,6 +341,7 @@
                 "server_set" : "Server-related settings",
                 "edit_set" : "Edit-related settings",
                 "communication_set" : "Communication-related settings",
+                "render_set" : "Rendering-related settings",
     
                 "namumark_fully_compatible_mode" : "namumark design compatible mode",
                 "wiki_name" : "Wikis name",
@@ -352,6 +376,8 @@
                 "set_wiki_access_password_need" : "Password required for wiki access",
                 "set_wiki_access_password" : "Wiki access password",
                 "set_history_recording_off" : "Stop recording history",
+                "link_case_insensitive" : "Link case insensitive",
+                "hide_user_name" : "Hide member name",
             "_comment_" : "Text",
                 "register_text" : "Terms of sign-up",
                 "non_login_alert" : "Non-login alert",
@@ -557,6 +583,7 @@
             "error_title_length_too_long" : "Documents title or Discussion topic length is too long. Maximum number of characters : ",
             "error_password_length_too_short" : "Password length is too short. Minimum number of characters : ",
             "error_password_require_for_wiki_access" : "A password is required to access the wiki.",
+            "timeout_error" : "Running is taking too long. Maximum running time (Second(s)) : ",
         "_comment_3.2_" : "Warning",
             "http_warning" : "Warning: If you are not on HTTPS connection, your information can be leaked. The users themselves have responsibility to any problems that happen because of this.",
             "user_head_warning" : "User data will be deleted if you close the browser or when you sign in.",

+ 29 - 4
lang/ko-KR.json

@@ -84,7 +84,7 @@
     "file_name_error": "파일명에는 알파벳, 한글, 공백, 밑줄 및 빼기 기호만 사용할 수 있습니다.",
     "pass": "넘기기",
     "recaptcha_error": "'로봇이 아닙니다'를 통해 reCAPTCHA를 통과하세요.",
-    "file_capacity_error": "최대 파일 크기 (MB): ",
+    "file_capacity_error": "최대 파일 크기 (MB) : ",
     "setting": "설정",
     "end": "끝",
     "error": "오류",
@@ -307,7 +307,7 @@
     "requires_approval": "가입시 승인 필요",
     "approval_question": "회원가입 질문",
     "public_key": "공개 키",
-    "fast_edit_error": "편집 속도가 너무 빠릅니다. 제한 (초): ",
+    "fast_edit_error": "편집 속도가 너무 빠릅니다. 제한 (초) : ",
     "main_acl_setting": "기본 ACL 설정",
     "edit_req_acl": "편집 요청 ACL",
     "application_list": "가입신청 목록",
@@ -528,6 +528,31 @@
     "spread": "펼침",
     "popup": "팝업",
     "popover": "팝오버",
-    "user_fix" : "사용자 수정",
-    "2fa_off" : "2FA 끄기"
+    "user_fix": "사용자 수정",
+    "2fa_off": "2FA 끄기",
+    "bbs": "게시판",
+    "bbs_main": "게시판 메인",
+    "bbs_name": "게시판 이름",
+    "bbs_make": "게시판 생성",
+    "order": "순번",
+    "comment_base": "댓글 기반",
+    "thread_base": "스레드 기반",
+    "bbs_set": "게시판 설정",
+    "bbs_view_acl": "게시판 읽기 ACL",
+    "bbs_acl": "게시판 ACL",
+    "bbs_edit_acl": "게시판 글 작성 ACL",
+    "bbs_comment_acl": "게시판 댓글 작성 ACL",
+    "sub_user_name": "보조 사용자 이름",
+    "render_set": "렌더링 관련 설정",
+    "link_case_insensitive": "링크 대소문자 구분 안함",
+    "hide_user_name": "가입자 이름 숨기기",
+    "comment": "댓글",
+    "reply": "대댓글",
+    "timeout_error": "실행 시간이 너무 오래 걸립니다. 최대 실행 시간 (초) : ",
+    "footnote_render": "각주 렌더링",
+    "footnote_number": "각주 번호 출력",
+    "only_number": "숫자만",
+    "footnote_real_num_view": "각주 실제 번호 보기",
+    "post_edit" : "게시글 수정",
+    "post_add" : "게시글 추가"
 }

+ 1 - 2
readme-en.md

@@ -1,8 +1,7 @@
 [(en-US)](./readme-en.md) | [(ko-KR)](./readme.md)
 
 # openNAMU
-[![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/)
+[![Up to Python 3.8](https://img.shields.io/badge/python->=%203.8-blue.svg)](https://python.org)
 [![LICENSE](https://img.shields.io/badge/license-BSD%203--Clause-lightgrey.svg)](./LICENSE)
 
 ![](https://raw.githubusercontent.com/openNAMU/openNAMU/beta/.github/logo.png)

+ 1 - 2
readme.md

@@ -1,8 +1,7 @@
 [(en-US)](./readme-en.md) | [(ko-KR)](./readme.md)
 
 # 오픈나무
-[![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/)
+[![Python 3.8 이상](https://img.shields.io/badge/python->=%203.8-blue.svg)](https://python.org)
 [![라이선스](https://img.shields.io/badge/license-BSD%203--Clause-lightgrey.svg)](./LICENSE)
 
 ![](https://raw.githubusercontent.com/openNAMU/openNAMU/beta/.github/logo.png)

+ 2 - 0
requirements.txt

@@ -1,3 +1,5 @@
+pip
+
 flask
 waitress
 

+ 5 - 3
route/__init__.py

@@ -12,8 +12,10 @@ from route.api_topic import api_topic
 from route.api_user_info import api_user_info
 from route.api_version import api_version
 from route.api_w import api_w
+from route.api_bbs_w_post import api_bbs_w_post
+from route.api_bbs_w_comment import api_bbs_w_comment
 
-from route.bbs_edit import bbs_edit
+from route.bbs_w_edit import bbs_w_edit
 from route.bbs_main import bbs_main
 from route.bbs_make import bbs_make
 from route.bbs_w import bbs_w
@@ -40,8 +42,6 @@ from route.give_admin_groups import give_admin_groups_2
 from route.give_auth import give_auth
 from route.give_delete_admin_group import give_delete_admin_group_2
 from route.give_user_ban import give_user_ban
-from route.give_user_check import give_user_check_2
-from route.give_user_check_delete import give_user_check_delete_2
 from route.give_user_fix import give_user_fix
 
 from route.list_acl import list_acl
@@ -54,6 +54,8 @@ from route.list_old_page import list_old_page
 from route.list_please import list_please
 from route.list_title_index import list_title_index
 from route.list_user import list_user
+from route.list_user_check import list_user_check
+from route.list_user_check_delete import list_user_check_delete
 
 from route.login_find import login_find
 from route.login_find_email import login_find_email

+ 29 - 0
route/api_bbs_w_comment.py

@@ -0,0 +1,29 @@
+from .tool.func import *
+
+def api_bbs_w_comment(sub_code = ''):
+    with get_db_connect() as conn:
+        curs = conn.cursor()
+
+        curs.execute(db_change('select set_name, set_data, set_code, set_id from bbs_data where (set_name = "comment" or set_name = "comment_date" or set_name = "comment_user_id") and set_id = ? order by set_code + 0 asc'), [sub_code])
+        db_data = curs.fetchall()
+        if not db_data:
+            return flask.jsonify({})
+        else:
+            temp_id = ''
+            temp_dict = {}
+            temp_list = []
+
+            for for_a in db_data:
+                if temp_id != for_a[2]:
+                    if temp_dict != {}:
+                        temp_list += [dict(temp_dict)]
+
+                    temp_id = for_a[2]
+                    temp_dict['code'] = for_a[2]
+
+                temp_dict[for_a[0]] = for_a[1]
+
+            if temp_dict != {}:
+                temp_list += [dict(temp_dict)]
+
+            return flask.jsonify(temp_list)

+ 26 - 0
route/api_bbs_w_post.py

@@ -0,0 +1,26 @@
+from .tool.func import *
+
+def api_bbs_w_post(sub_code = ''):
+    sub_code_split = sub_code.split('-')
+    if len(sub_code_split) < 2:
+        sub_code_split = ['', '']
+
+    with get_db_connect() as conn:
+        curs = conn.cursor()
+
+        curs.execute(db_change('select set_name, set_data, set_code from bbs_data where set_id = ? and set_code = ?'), [sub_code_split[0], sub_code_split[1]])
+        db_data = curs.fetchall()
+        if not db_data:
+            return flask.jsonify({})
+        else:
+            temp_id = ''
+            temp_dict = {}
+
+            for for_a in db_data:
+                if temp_id != for_a[2]:
+                    temp_id = for_a[2]
+                    temp_dict['code'] = for_a[2]
+
+                temp_dict[for_a[0]] = for_a[1]
+
+            return flask.jsonify(temp_dict)

+ 2 - 1
route/api_skin_info.py

@@ -1,3 +1,5 @@
+import urllib.request
+
 from .tool.func import *
 
 def api_skin_info(name = ''):
@@ -59,7 +61,6 @@ def api_skin_info(name = ''):
                                     "skin_ver" : get_data["skin_ver"]
                                 }}}
 
-
                     a_data = {**a_data, **{ i : json_data }}
 
             return flask.jsonify(a_data)

+ 110 - 15
route/api_topic.py

@@ -1,5 +1,111 @@
 from .tool.func import *
-from .bbs_w_post import bbs_w_post_make_thread
+
+def api_topic_thread_make(user_id, date, data, code, color = '', blind = '', add_style = ''):
+    if blind != '':
+        if data == '':
+            color_b = 'opennamu_comment_blind'
+        else:
+            color_b = 'opennamu_comment_blind_admin'
+    else:
+        color_b = 'opennamu_comment_blind_not'
+
+    return '''
+        <table class="opennamu_comment" style="''' + add_style + '''">
+            <tr>
+                <td class="opennamu_comment_color_''' + color + '''">
+                    <a href="#thread_shortcut" id="''' + code + '''">#''' + code + '''</a>
+                    ''' + user_id + '''
+                    <span style="float: right;">''' + date + '''</span>
+                </td>
+            </tr>
+            <tr>
+                <td class="''' + color_b + '''" id="opennamu_comment_data_main">
+                    ''' + data + '''
+                </td>
+            </tr>
+        </table>
+    '''
+
+def api_topic_thread_pre_render(curs, data, num, ip, topic_num = '', name = '', sub = '', do_type = 'thread'):
+    # 이거 좀 엉성해서 언젠간 손 보고 싶음
+
+    call_thread_regex = r"( |\n|^)(?:#([0-9]+)(?:-([0-9]+))?)( |\n|$)"
+    call_thread_count = len(re.findall(call_thread_regex, data)) * 3
+    while 1:
+        rd_data = re.search(call_thread_regex, data)
+        if call_thread_count < 0:
+            break
+        elif not rd_data:
+            break
+        else:
+            rd_data = rd_data.groups()
+
+            view_data = rd_data[1]
+            send_topic_num = topic_num
+            if rd_data[2]:
+                view_data += '-' + rd_data[2]
+                if do_type == 'thread':
+                    send_topic_num = rd_data[2]
+                else:
+                    set_id = topic_num.split('-')
+
+                    send_topic_num = set_id[0] + '-' + rd_data[2]
+                    view_data += '-' + set_id[0]
+
+            if do_type == 'thread':
+                curs.execute(db_change("select ip from topic where code = ? and id = ?"), [send_topic_num, rd_data[1]])
+            else:
+                if rd_data[1] == '0':
+                    set_id = send_topic_num.split('-')
+                    set_id = ['', ''] if len(set_id) < 2 else set_id
+
+                    curs.execute(db_change('select set_data from bbs_data where set_name = "user_id" and set_id = ? and set_code = ?'), [set_id[0], set_id[1]])
+                else:
+                    curs.execute(db_change('select set_data from bbs_data where set_name = "comment_user_id" and set_id = ? and set_code = ?'), [send_topic_num, rd_data[1]])
+
+            ip_data = curs.fetchall()
+            if ip_data and ip_or_user(ip_data[0][0]) == 0:
+                if do_type == 'thread':
+                    add_alarm(ip_data[0][0], ip, '<a href="/thread/' + topic_num + '#' + num + '">' + html.escape(name) + ' - ' + html.escape(sub) + '#' + num + '</a>')
+                else:
+                    set_id = topic_num.split('-')
+                    set_id = ['', ''] if len(set_id) < 2 else set_id
+
+                    add_alarm(ip_data[0][0], ip, 'BBS <a href="/bbs/w/' + set_id[0] + '/' + set_id[1] + '#' + num + '">' + html.escape(name) + ' - ' + html.escape(sub) + '#' + num + '</a>')
+
+            data = re.sub(call_thread_regex, rd_data[0] + '<topic_a_' + do_type + '>#' + view_data + '</topic_a_' + do_type + '>' + rd_data[3], data, 1)
+
+        call_thread_count -= 1
+
+    call_user_regex = r"( |\n|^)(?:@([^ \n]+))( |\n|$)"
+    call_user_count = len(re.findall(call_user_regex, data)) * 3
+    while 1:
+        rd_data = re.search(call_user_regex, data)
+        if call_user_count < 0:
+            break
+        elif not rd_data:
+            break
+        else:
+            rd_data = rd_data.groups()
+
+            curs.execute(db_change("select ip from history where ip = ? limit 1"), [rd_data[1]])
+            ip_data = curs.fetchall()
+            if not ip_data:
+                curs.execute(db_change("select ip from topic where ip = ? limit 1"), [rd_data[1]])
+                ip_data = curs.fetchall()
+
+            if ip_data and ip_or_user(ip_data[0][0]) == 0:
+                if do_type == 'thread':
+                    add_alarm(ip_data[0][0], ip, '<a href="/thread/' + topic_num + '#' + num + '">' + html.escape(name) + ' - ' + html.escape(sub) + '#' + num + '</a>')
+                else:
+                    set_id = topic_num.split('-')
+                    add_alarm(ip_data[0][0], ip, 'BBS <a href="/bbs/w/' + set_id[0] + '/' + set_id[1] + '#' + num + '">' + html.escape(name) + ' - ' + html.escape(sub) + '#' + num + '</a>')
+
+            data = re.sub(call_user_regex, rd_data[0] + '<topic_call>@' + rd_data[1] + '</topic_call>' + rd_data[2], data, 1)
+
+        call_user_count -= 1
+
+    return data
 
 def api_topic(topic_num = 1, tool = 'normal', num = '', render = ''):
     with get_db_connect() as conn:
@@ -45,19 +151,8 @@ def api_topic(topic_num = 1, tool = 'normal', num = '', render = ''):
                     if data_v != '':
                         data_v = render_set(
                             doc_data = data_v, 
-                            data_type = 'api_view',
-                            data_in = 'topic_' + topic_num + '_' + i[0],
-                            doc_acl = 0
-                        )
-                        data_v[0] = re.sub(
-                            r'&lt;topic_a&gt;(?P<in>(?:(?!&lt;\/topic_a&gt;).)+)&lt;\/topic_a&gt;',
-                            '<a href="\g<in>">\g<in></a>',
-                            data_v[0]
-                        )
-                        data_v[0] = re.sub(
-                            r'&lt;topic_call&gt;@(?P<in>(?:(?!&lt;\/topic_call&gt;).)+)&lt;\/topic_call&gt;',
-                            '<a href="/w/user:\g<in>">@\g<in></a>',
-                            data_v[0]
+                            data_type = 'api_thread',
+                            data_in = 'topic_' + topic_num + '_' + i[0]
                         )
                     else:
                         data_v = ['', '']
@@ -88,7 +183,7 @@ def api_topic(topic_num = 1, tool = 'normal', num = '', render = ''):
                                 else:
                                     color = 'default'
 
-                            data_r += bbs_w_post_make_thread(
+                            data_r += api_topic_thread_make(
                                 for_a["ip_pas"],
                                 '<a href="/thread/' + topic_num + '/comment/' + for_a["id"] + '/tool">(' + load_lang('tool') + ')</a> ' + for_a["date"],
                                 for_a["data_pas"][0] + '<script>' + for_a["data_pas"][1] + '</script>',

+ 1 - 2
route/api_w.py

@@ -11,8 +11,7 @@ def api_w(name = 'Test', tool = '', rev = ''):
                 data_pas = render_set(
                     doc_name = name, 
                     doc_data = data_org, 
-                    data_type = 'api_view',
-                    data_in = ''
+                    data_type = 'api_view'
                 )
 
                 return flask.jsonify({

+ 7 - 1
route/bbs_main.py

@@ -15,12 +15,18 @@ def bbs_main():
                 curs.execute(db_change('select set_data from bbs_set where set_name = "bbs_type" and set_id = ?'), [for_a[1]])
                 db_data_2 = curs.fetchall()
                 bbs_type = db_data_2[0][0] if db_data_2 else 'comment'
+
+                if bbs_type == 'thread':
+                    bbs_type = load_lang('thread_base')
+                else:
+                    bbs_type = load_lang('comment_base')
                 
                 curs.execute(db_change('select set_data from bbs_data where set_id = ? and set_name = "date" order by set_code + 0 desc limit 1'), [for_a[1]])
                 db_data_2 = curs.fetchall()
                 last_date = ('(' + db_data_2[0][0] + ')') if db_data_2 else ''
 
-                data += '<li><a href="/bbs/w/' + for_a[1] + '">' + for_a[0] + ' (' + bbs_type + ') ' + last_date + '</a></li>'
+                data += '<li><a href="/bbs/w/' + for_a[1] + '">' + html.escape(for_a[0]) + ' (' + bbs_type + ') ' + last_date + '</a></li>'
+                # data += '<li></li>'
 
             data += '</ul>'
 

+ 2 - 2
route/bbs_make.py

@@ -32,8 +32,8 @@ def bbs_make():
                         <hr class="main_hr">
                         
                         <select name="bbs_type">
-                            <option value="comment">''' + load_lang('comment') + '''</option>
-                            <option value="thread">''' + load_lang('thread') + '''</option>
+                            <option value="comment">''' + load_lang('comment_base') + '''</option>
+                            <option value="thread">''' + load_lang('thread_base') + '''</option>
                         </select>
                         <hr class="main_hr">
                         

+ 11 - 11
route/bbs_w.py

@@ -4,17 +4,19 @@ def bbs_w(bbs_num = ''):
     with get_db_connect() as conn:
         curs = conn.cursor()
 
-        curs.execute(db_change('select set_id from bbs_set where set_id = ? and set_name = "bbs_name"'), [bbs_num])
-        if not curs.fetchall():
+        curs.execute(db_change('select set_data from bbs_set where set_id = ? and set_name = "bbs_name"'), [bbs_num])
+        db_data = curs.fetchall()
+        if not db_data:
             return redirect('/bbs/main')
-
+        
+        bbs_name = db_data[0][0]
         bbs_num_str = str(bbs_num)
 
         data = ''
         data += '''
             <table id="main_table_set">
                 <tr id="main_table_top_tr">
-                    <td id="main_table_width">''' + load_lang('version') + '''</td>
+                    <td id="main_table_width">''' + load_lang('order') + '''</td>
                     <td id="main_table_width">''' + load_lang('editor') + '''</td>
                     <td id="main_table_width">''' + load_lang('time') + '''</td>
                 </tr>
@@ -30,10 +32,9 @@ def bbs_w(bbs_num = ''):
         for for_a in db_data + [['', '', '']]:
             if temp_id != for_a[2]:
                 if temp_id != '':
-                    curs.execute(db_change('select set_data from bbs_data where set_name = "comment_date" and set_id = ? order by set_code + 0 desc'), [bbs_num_str + '-' + temp_dict['code']])
+                    curs.execute(db_change('select count(*) from bbs_data where set_name = "comment_date" and (set_id = ? or set_id like ?) order by set_code + 0 desc'), [bbs_num_str + '-' + temp_dict['code'], bbs_num_str + '-' + temp_dict['code'] + '-%'])
                     db_data = curs.fetchall()
-                    last_comment_date = db_data[0][0] if db_data else '0'
-                    comment_count = str(len(db_data)) if db_data else '0'
+                    comment_count = str(db_data[0][0]) if db_data else '0'
                     
                     data += '''
                         <tr>
@@ -43,9 +44,8 @@ def bbs_w(bbs_num = ''):
                         </tr>
                         <tr>
                             <td colspan="3">
-                                <a href="/bbs/w/''' + bbs_num_str + '/' + temp_dict['code'] + '">' + temp_dict['title'] + '''</a> 
+                                <a href="/bbs/w/''' + bbs_num_str + '/' + temp_dict['code'] + '">' + html.escape(temp_dict['title']) + '''</a> 
                                 (''' + comment_count + ''') 
-                                (''' + last_comment_date + ''')
                             </td>
                         </tr>
                     '''
@@ -58,7 +58,7 @@ def bbs_w(bbs_num = ''):
         data += '</table>'
 
         return easy_minify(flask.render_template(skin_check(),
-            imp = [load_lang('bbs_main'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
+            imp = [bbs_name, wiki_set(), wiki_custom(), wiki_css(['(' + load_lang('bbs') + ')', 0])],
             data = data,
-            menu = [['bbs/main', load_lang('return')], ['bbs/edit/' + bbs_num_str, load_lang('add')], ['bbs/set/' + bbs_num_str, load_lang('setting')]]
+            menu = [['bbs/main', load_lang('return')], ['bbs/edit/' + bbs_num_str, load_lang('add')], ['bbs/set/' + bbs_num_str, load_lang('bbs_set')]]
         ))

+ 51 - 58
route/bbs_edit.py → route/bbs_w_edit.py

@@ -1,6 +1,9 @@
 from .tool.func import *
 
-def bbs_edit(bbs_num = '', post_num = '', do_type = ''):
+from .api_bbs_w_post import api_bbs_w_post
+from .edit import edit_editor
+
+def bbs_w_edit(bbs_num = '', post_num = '', do_type = ''):
     with get_db_connect() as conn:
         curs = conn.cursor()
 
@@ -24,6 +27,8 @@ def bbs_edit(bbs_num = '', post_num = '', do_type = ''):
             
         if acl_check(bbs_num_str, 'bbs_edit') == 1:
             return redirect('/bbs/set/' + bbs_num_str)
+        
+        i_list = ['post_view_acl', 'post_comment_acl']
 
         if flask.request.method == 'POST' and do_type != 'preview':
             if captcha_post(flask.request.form.get('g-recaptcha-response', flask.request.form.get('g-recaptcha', ''))) == 1:
@@ -59,16 +64,18 @@ def bbs_edit(bbs_num = '', post_num = '', do_type = ''):
 
             return redirect('/bbs/w/' + bbs_num_str + '/' + id_data)
         else:
+            d_list = ['' for _ in range(0, len(i_list))]
             if do_type == 'preview':
                 title = flask.request.form.get('title', '')
                 data = flask.request.form.get('content', '')
                 data = data.replace('\r', '')
-
                 data_preview = render_set(
-                    doc_name = '', 
                     doc_data = data,
-                    data_in = 'from'
-                )
+                    data_type = 'thread',
+                    data_in = 'bbs'
+                ) + '<hr>'
+                for for_a in range(0, len(i_list)):
+                    d_list[for_a] = flask.request.form.get(i_list[for_a], 'normal')
             else:
                 if post_num == '':
                     title = ''
@@ -79,19 +86,22 @@ def bbs_edit(bbs_num = '', post_num = '', do_type = ''):
                     db_data = curs.fetchall()
                     db_data = list(db_data) if db_data else []
 
-                    temp_id = ''
-                    temp_dict = {}
-
-                    for for_a in db_data + [['', '', '']]:
-                        if temp_id != for_a[2]:
-                            temp_id = for_a[2]
-                            temp_dict['code'] = for_a[2]
-
-                        temp_dict[for_a[0]] = for_a[1]
+                    temp_dict = json.loads(api_bbs_w_post(bbs_num_str + '-' + post_num_str).data)
 
                     title = temp_dict['title']
                     data = temp_dict['data']
                     data_preview = ''
+
+            acl_div = ['' for _ in range(0, len(i_list))]
+            acl_list = get_acl_list()
+            for for_a in range(0, len(i_list)):
+                for data_list in acl_list:
+                    if data_list == d_list[for_a]:
+                        check = 'selected="selected"'
+                    else:
+                        check = ''
+
+                    acl_div[for_a] += '<option value="' + data_list + '" ' + check + '>' + (data_list if data_list != '' else 'normal') + '</option>'
             
             if post_num == '':
                 form_action = 'formaction="/bbs/edit/' + bbs_num_str + '"'
@@ -101,64 +111,47 @@ def bbs_edit(bbs_num = '', post_num = '', do_type = ''):
                 form_action_preview = 'formaction="/bbs/edit/preview/' + bbs_num_str + '/' + post_num_str + '"'
     
             editor_top_text = '<a href="/edit_filter">(' + load_lang('edit_filter_rule') + ')</a>'
-            
-            monaco_on = get_main_skin_set(curs, flask.session, 'main_css_monaco', ip)
-            if monaco_on == 'use':
-                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.37.1/min/vs/editor/editor.main.min.css">
-                    <script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.37.1/min/vs/loader.min.js"></script>
-                '''
-
-                editor_top_text += ' <a href="javascript:opennamu_edit_turn_off_monaco();">(' + load_lang('turn_off_monaco') + ')</a>'
-                
-                if flask.request.cookies.get('main_css_darkmode', '0') == '1':
-                    monaco_thema = 'vs-dark'
-                else:
-                    monaco_thema = ''
-                
-                add_script = 'do_monaco_init("' + monaco_thema + '");'
-            else:
-                editor_display = ''
-                monaco_display = 'style="display: none;"'
-                add_get_file = ''
-                add_script = 'opennamu_edit_turn_off_monaco();'
 
             if editor_top_text != '':
                 editor_top_text += '<hr class="main_hr">'
+
+            if post_num == '':
+                bbs_title = load_lang('post_add')
+            else:
+                bbs_title = load_lang('post_edit')
     
             return easy_minify(flask.render_template(skin_check(), 
-                imp = [load_lang('bbs_edit'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
-                data =  editor_top_text + add_get_file + '''
-                    <form method="post">
-                        <textarea style="display: none;" id="opennamu_edit_origin" name="doc_data_org"></textarea>
-
-                        <div>''' + edit_button('opennamu_edit_textarea', 'opennamu_monaco_editor') + '''</div>
-                        
-                        <input placeholder="''' + load_lang('bbs_title') + '''" name="title" value="''' + html.escape(title) + '''">
+                imp = [bbs_title, wiki_set(), wiki_custom(), wiki_css([0, 0])],
+                data =  editor_top_text + '''
+                    <form method="post">                        
+                        <input placeholder="''' + load_lang('title') + '''" name="title" value="''' + html.escape(title) + '''">
                         <hr class="main_hr">
 
-                        <div id="opennamu_monaco_editor" class="opennamu_textarea_500" ''' + monaco_display + '''></div>
-                        <textarea id="opennamu_edit_textarea" ''' + editor_display + ''' class="opennamu_textarea_500" name="content">''' + html.escape(data) + '''</textarea>
+                        ''' + edit_editor(curs, ip, data) + '''
                         <hr class="main_hr">
                         
                         ''' + captcha_get() + ip_warning() + '''
                         
                         <button id="opennamu_save_button" type="submit" ''' + form_action + ''' onclick="do_monaco_to_textarea(); do_stop_exit_release();">''' + load_lang('save') + '''</button>
                         <button id="opennamu_preview_button" type="submit" ''' + form_action_preview + ''' onclick="do_monaco_to_textarea(); do_stop_exit_release();">''' + load_lang('preview') + '''</button>
-                    </form>
-                    
-                    <hr class="main_hr">
-                    <div id="opennamu_preview_area">''' + data_preview + '''</div>
                     
-                    <script>
-                        do_stop_exit();
-                        do_paste_image('opennamu_edit_textarea', 'opennamu_monaco_editor');
-                        ''' + add_script + '''
-                    </script>
+                        <hr class="main_hr">
+                        <div id="opennamu_preview_area">''' + data_preview + '''</div>
+
+                        ''' + render_simple_set('''
+                            <hr class="main_hr">
+                            <a href="/acl/TEST#exp">(''' + load_lang('reference') + ''')</a>
+                            <h2>''' + load_lang('acl') + '''</h2>
+                            <h3>''' + load_lang('post_view_acl') + '''</h3>
+                            <select name="post_view_acl">''' + acl_div[0] + '''</select>
+
+                            <h4>''' + load_lang('post_comment_acl') + '''</h4>
+                            <select name="post_comment_acl">''' + acl_div[1] + '''</select>
+
+                            <h2>''' + load_lang('markup') + '''</h2>
+                            ''' + load_lang('not_working') + '''
+                        ''') + '''
+                    </form>
                 ''',
                 menu = [['bbs/w/' + bbs_num_str, load_lang('return')]]
             ))

+ 184 - 195
route/bbs_w_post.py

@@ -1,44 +1,83 @@
 from .tool.func import *
 
-def bbs_w_post_make_thread(user_id, date, data, code, color = '', blind = '', add_style = ''):
-    if blind != '':
-        if data == '':
-            color_b = 'opennamu_comment_blind'
-        else:
-            color_b = 'opennamu_comment_blind_admin'
-    else:
-        color_b = 'opennamu_comment_blind_not'
-
-    return '''
-        <table class="opennamu_comment" style="''' + add_style + '''">
-            <tr>
-                <td class="opennamu_comment_color_''' + color + '''">
-                    <a href="#thread_shortcut" id="''' + code + '''">#''' + code + '''</a>
-                    ''' + user_id + '''
-                    <span style="float: right;">''' + date + '''</span>
-                </td>
-            </tr>
-            <tr>
-                <td class="''' + color_b + '''" id="opennamu_comment_data_main">
-                    ''' + data + '''
-                </td>
-            </tr>
-        </table>
-    '''
+from .api_bbs_w_post import api_bbs_w_post
+from .api_bbs_w_comment import api_bbs_w_comment
+from .api_topic import api_topic_thread_make, api_topic_thread_pre_render
+
+from .edit import edit_editor
+
+def bbs_w_post_comment(user_id, sub_code, comment_num, bbs_num_str, post_num_str):
+    comment_data = ''
+    comment_select = ''
+
+    comment_count = 0
+    comment_add_count = 0
+
+    thread_data = json.loads(api_bbs_w_comment(sub_code).data)
+    
+    comment_count += len(thread_data)
+    comment_add_count += comment_count
+
+    for temp_dict in thread_data:
+        color = 'default'
+        if user_id == temp_dict['comment_user_id']:
+            color = 'green'
+
+        sub_code_check = re.sub(r'^[0-9]+-[0-9]+-', '', sub_code + '-' + temp_dict['code'])
+        margin_count = sub_code_check.count('-')
+
+        date = ''
+        date += '<a href="javascript:opennamu_change_comment(\'' + sub_code_check + '\');">(' + load_lang('comment') + ')</a> '
+        date += '<a href="/bbs/w/' + bbs_num_str + '/' + post_num_str + '/comment/' + sub_code_check + '/tool">(' + load_lang('tool') + ')</a> '
+        date += temp_dict['comment_date']
+
+        comment_data += '<span style="padding-left: 20px;"></span>' * margin_count
+        comment_data += api_topic_thread_make(
+            ip_pas(temp_dict['comment_user_id']),
+            date,
+            render_set(
+                doc_data = temp_dict['comment'],
+                data_in = 'bbs_comment_' + sub_code_check
+            ),
+            sub_code_check,
+            color = color,
+            add_style = 'width: calc(100% - ' + str(margin_count * 20) + 'px);'
+        )
+
+        comment_default = ''
+        if comment_num == sub_code_check:
+            comment_default = 'selected'
+
+        comment_select += '<option value="' + sub_code_check + '" ' + comment_default + '>' + sub_code_check + '</option>'
+        comment_data += '<hr class="main_hr">'
+
+        temp_data = bbs_w_post_comment(user_id, sub_code + '-' + temp_dict['code'], comment_num, bbs_num_str, post_num_str)
+
+        comment_data += temp_data[0]
+        comment_select += temp_data[1]
+        comment_add_count += temp_data[3]
+
+    return (comment_data, comment_select, comment_count, comment_add_count)
 
 def bbs_w_post(bbs_num = '', post_num = '', do_type = ''):
     with get_db_connect() as conn:
         curs = conn.cursor()
 
-        curs.execute(db_change('select set_name, set_data, set_code from bbs_data where set_id = ? and set_code = ?'), [bbs_num, post_num])
-        db_data = curs.fetchall()
-        if not db_data:
+        curs.execute(db_change('select set_data from bbs_set where set_id = ? and set_name = "bbs_name"'), [bbs_num])
+        db_data_3 = curs.fetchall()
+        if not db_data_3:
             return redirect('/bbs/main')
+        
+        bbs_name = db_data_3[0][0]
 
         bbs_num_str = str(bbs_num)
         post_num_str = str(post_num)
         bbs_comment_acl = acl_check(bbs_num_str, 'bbs_comment')
         ip = ip_check()
+
+        temp_dict = json.loads(api_bbs_w_post(bbs_num_str + '-' + post_num_str).data)
+        if temp_dict == {}:
+            return redirect('/bbs/main')
         
         curs.execute(db_change('select set_data from bbs_set where set_id = ? and set_name = "bbs_type"'), [bbs_num])
         db_data_2 = curs.fetchall()
@@ -57,116 +96,101 @@ def bbs_w_post(bbs_num = '', post_num = '', do_type = ''):
                 set_id = bbs_num_str + '-' + post_num_str
 
                 curs.execute(db_change('select set_code from bbs_data where set_name = "comment" and set_id = ? order by set_code + 0 desc'), [set_id])
-                db_data = curs.fetchall()
-                id_data = str(int(db_data[0][0]) + 1) if db_data else '1'
+                db_data_4 = curs.fetchall()
+                id_data = str(int(db_data_4[0][0]) + 1) if db_data_4 else '1'
 
                 data = flask.request.form.get('content', '')
                 if data == '':
                     # re_error로 대체 예정
                     return redirect('/bbs/w/' + bbs_num_str + '/' + post_num_str)
                 
+                data = data.replace('\r', '')
+                data = api_topic_thread_pre_render(curs, data, id_data, ip, set_id, bbs_name, temp_dict['title'], 'post')
+                
                 date = get_time()
 
                 curs.execute(db_change("insert into bbs_data (set_name, set_code, set_id, set_data) values ('comment', ?, ?, ?)"), [id_data, set_id, data])
                 curs.execute(db_change("insert into bbs_data (set_name, set_code, set_id, set_data) values ('comment_date', ?, ?, ?)"), [id_data, set_id, date])
                 curs.execute(db_change("insert into bbs_data (set_name, set_code, set_id, set_data) values ('comment_user_id', ?, ?, ?)"), [id_data, set_id, ip])
 
+                add_alarm(temp_dict['user_id'], ip, 'BBS <a href="/bbs/w/' + bbs_num_str + '/' + post_num_str + '#' + id_data + '">' + html.escape(bbs_name) + ' - ' + html.escape(temp_dict['title']) + '#' + id_data + '</a>')
+
                 conn.commit()
 
-                return redirect('/bbs/w/' + bbs_num_str + '/' + post_num_str + '#comment_' + str(int(id_data) + 1))
+                return redirect('/bbs/w/' + bbs_num_str + '/' + post_num_str + '#' + id_data)
             else:
                 if acl_check(bbs_num_str, 'bbs_view') == 1:
                     return re_error('/ban')
 
+                text = ''
+                data_preview = ''
                 if do_type == 'preview':
                     text = flask.request.form.get('content', '')
                     text = text.replace('\r', '')
 
                     data_preview = render_set(
-                        doc_name = '', 
                         doc_data = text,
-                        data_in = 'from'
+                        data_type = 'thread',
+                        data_in = 'bbs_comment_preview'
                     )
-                else:
-                    text = ''
-                    data_preview = ''
-                
-                temp_id = ''
-                temp_dict = {}
-
-                db_data = list(db_data) if db_data else []
-                for for_a in db_data + [['', '', '']]:
-                    if temp_id != for_a[2]:
-                        temp_id = for_a[2]
-                        temp_dict['code'] = for_a[2]
 
-                    temp_dict[for_a[0]] = for_a[1]
-
-                count = 1
+                date = ''
+                date += '<a href="/bbs/w/' + bbs_num_str + '/' + post_num_str + '/tool">(' + load_lang('tool') + ')</a> '
+                date += temp_dict['date']
 
                 data = ''
                 data += '<h2>' + html.escape(temp_dict['title']) + '</h2>'
-                data += bbs_w_post_make_thread(
+                data += api_topic_thread_make(
                     ip_pas(temp_dict['user_id']),
-                    temp_dict['date'],
+                    date,
                     render_set(
-                        doc_name = '', 
                         doc_data = temp_dict['data'],
-                        data_in = 'from'
+                        data_type = 'thread',
+                        data_in = 'bbs'
                     ),
-                    '1',
+                    '0',
                     color = 'green'
                 )
                 data += '<hr class="main_hr">'
 
                 user_id = temp_dict['user_id']
+                count = 0
 
-                temp_id = ''
-                temp_dict = {}
-
-                curs.execute(db_change('select set_name, set_data, set_code, set_id from bbs_data where (set_name = "comment" or set_name = "comment_date" or set_name = "comment_user_id") and set_id = ? order by set_code + 0 asc'), [bbs_num_str + '-' + post_num_str])
-                db_data = curs.fetchall()
-                db_data = list(db_data) if db_data else []
-
-                for for_a in db_data + [['', '', '']]:
-                    if temp_id == '':
-                        temp_id = for_a[2]
-
-                    if temp_id != for_a[2]:
-                        temp_id = for_a[2]
-                        temp_dict['code'] = for_a[2]
-                        count += 1
-
-                        if user_id == temp_dict['comment_user_id']:
-                            color = 'green'
-                        else:
-                            color = 'default'
-
-                        data += bbs_w_post_make_thread(
-                            ip_pas(temp_dict['comment_user_id']),
-                            temp_dict['comment_date'],
-                            render_set(
-                                doc_name = '', 
-                                doc_data = temp_dict['comment'],
-                                data_in = 'from'
-                            ),
-                            str(count),
-                            color = color
-                        )
-                        data += '<hr class="main_hr">'
-
-                    temp_dict[for_a[0]] = for_a[1]
+                thread_data = json.loads(api_bbs_w_comment(bbs_num_str + '-' + post_num_str).data)
+                for temp_dict in thread_data:
+                    count += 1
+                    if user_id == temp_dict['comment_user_id']:
+                        color = 'green'
+                    else:
+                        color = 'default'
+                        
+                    date = ''
+                    date += '<a href="/bbs/w/' + bbs_num_str + '/' + post_num_str + '/comment/' + str(count) + '/tool">(' + load_lang('tool') + ')</a> '
+                    date += temp_dict['comment_date']
+
+                    data += api_topic_thread_make(
+                        ip_pas(temp_dict['comment_user_id']),
+                        date,
+                        render_set(
+                            doc_data = temp_dict['comment'],
+                            data_type = 'thread',
+                            data_in = 'bbs_comment_' + str(count)
+                        ),
+                        str(count),
+                        color = color
+                    )
+                    data += '<hr class="main_hr">'
 
                 bbs_comment_form = ''
                 if bbs_comment_acl == 0:
-                    bbs_comment_form = '''                        
-                        <textarea name="content" id="opennamu_edit_textarea" class="opennamu_textarea_100">''' + html.escape(text) + '''</textarea>
+                    bbs_comment_form += '''                        
+                        ''' + edit_editor(curs, ip, text, 'thread') + '''
                         <hr class="main_hr">
                         
                         ''' + captcha_get() + ip_warning() + '''
 
-                        <button id="opennamu_save_button" formaction="/bbs/w/''' + bbs_num_str + '''/''' + post_num_str + '''" type="submit">''' + load_lang('send') + '''</button>
-                        <button id="opennamu_preview_button" formaction="/bbs/w/preview/''' + bbs_num_str + '''/''' + post_num_str + '''#opennamu_edit_textarea" type="submit">''' + load_lang('preview') + '''</button>
+                        <button id="opennamu_save_button" formaction="/bbs/w/''' + bbs_num_str + '''/''' + post_num_str + '''" type="submit" onclick="do_monaco_to_textarea(); do_stop_exit_release();">''' + load_lang('send') + '''</button>
+                        <button id="opennamu_preview_button" formaction="/bbs/w/preview/''' + bbs_num_str + '''/''' + post_num_str + '''#opennamu_edit_textarea" type="submit" onclick="do_monaco_to_textarea(); do_stop_exit_release();">''' + load_lang('preview') + '''</button>
                         <hr class="main_hr">
                     '''
 
@@ -178,7 +202,7 @@ def bbs_w_post(bbs_num = '', post_num = '', do_type = ''):
                 '''
 
                 return easy_minify(flask.render_template(skin_check(),
-                    imp = [load_lang('bbs_main'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
+                    imp = [bbs_name, wiki_set(), wiki_custom(), wiki_css(['(' + load_lang('bbs') + ')', 0])],
                     data = data,
                     menu = [['bbs/w/' + bbs_num_str, load_lang('return')], ['bbs/edit/' + bbs_num_str + '/' + post_num_str, load_lang('edit')]]
                 ))
@@ -193,28 +217,36 @@ def bbs_w_post(bbs_num = '', post_num = '', do_type = ''):
                 else:
                     captcha_post('', 0)
                 
-                select = flask.request.form.get('comment_select', 'default')
-                select = '' if select == 'default' else select
+                select = flask.request.form.get('comment_select', '0')
+                select = '' if select == '0' else select
+
+                comment_user_name = ''
+
                 if select != '':
-                    select = select.split('-')
-                    if len(select) < 2:
-                        curs.execute(db_change('select set_code from bbs_data where set_name = "comment" and set_id = ? and set_code = ? limit 1'), [bbs_num_str + '-' + post_num_str, select[0]])
-                        if not curs.fetchall():
-                            return ''
+                    select_split = select.split('-')
+                    if len(select_split) < 2:
+                        curs.execute(db_change('select set_data from bbs_data where set_name = "comment_user_id" and set_id = ? and set_code = ? limit 1'), [bbs_num_str + '-' + post_num_str, select_split[0]])    
+                        db_data_6 = curs.fetchall()
+                        if not db_data_6:
+                            # re_error로 변경 예정
+                            return redirect('/bbs/w/' + bbs_num_str + '/' + post_num_str)
                         else:
-                            set_id = bbs_num_str + '-' + post_num_str + '-' + select[0]
+                            set_id = bbs_num_str + '-' + post_num_str + '-' + select_split[0]
+                            comment_user_name = db_data_6[0][0]
                     else:
-                        curs.execute(db_change('select set_code from bbs_data where set_name = "comment" and set_id = ? and set_code = ? limit 1'), [bbs_num_str + '-' + post_num_str + '-' + '-'.join(select[0:len(select) - 1]), select[len(select) - 1]])
-                        if not curs.fetchall():
-                            return ''
+                        curs.execute(db_change('select set_data from bbs_data where set_name = "comment_user_id" and set_id = ? and set_code = ? limit 1'), [bbs_num_str + '-' + post_num_str + '-' + '-'.join(select_split[0:len(select_split) - 1]), select_split[len(select_split) - 1]])
+                        db_data_7 = curs.fetchall()
+                        if not db_data_7:
+                            return redirect('/bbs/w/' + bbs_num_str + '/' + post_num_str)
                         else:
-                            set_id = bbs_num_str + '-' + post_num_str + '-' + '-'.join(select)
+                            set_id = bbs_num_str + '-' + post_num_str + '-' + '-'.join(select_split)
+                            comment_user_name = db_data_7[0][0]
                 else:
                     set_id = bbs_num_str + '-' + post_num_str
 
-                curs.execute(db_change('select set_code from bbs_data where set_name = "comment" and set_id = ? order by set_code + 0 desc'), [set_id])
-                db_data = curs.fetchall()
-                id_data = str(int(db_data[0][0]) + 1) if db_data else '1'
+                curs.execute(db_change('select set_code from bbs_data where set_name = "comment" and set_id = ? order by set_code + 0 desc limit 1'), [set_id])
+                db_data_5 = curs.fetchall()
+                id_data = str(int(db_data_5[0][0]) + 1) if db_data_5 else '1'
 
                 data = flask.request.form.get('content', '')
                 if data == '':
@@ -230,138 +262,94 @@ def bbs_w_post(bbs_num = '', post_num = '', do_type = ''):
                 conn.commit()
             
                 if set_id == '':
-                    return redirect('/bbs/w/' + bbs_num_str + '/' + post_num_str + '#comment_' + id_data)
+                    end_id = id_data
                 else:
                     set_id = re.sub(r'^[0-9]+-[0-9]+-?', '', set_id)
-                    return redirect('/bbs/w/' + bbs_num_str + '/' + post_num_str + '#comment_' + set_id + '-' + id_data)
+                    set_id += '-' if set_id != '' else ''
+                    end_id = set_id + id_data
+
+                add_alarm(temp_dict['user_id'], ip, 'BBS <a href="/bbs/w/' + bbs_num_str + '/' + post_num_str + '#' + end_id + '">' + html.escape(bbs_name) + ' - ' + html.escape(temp_dict['title']) + '#' + end_id + '</a>')
+                if comment_user_name != '':
+                    add_alarm(comment_user_name, ip, 'BBS <a href="/bbs/w/' + bbs_num_str + '/' + post_num_str + '#' + end_id + '">' + html.escape(bbs_name) + ' - ' + html.escape(temp_dict['title']) + '#' + end_id + '</a>')
+
+                return redirect('/bbs/w/' + bbs_num_str + '/' + post_num_str + '#' + end_id)
             else:
                 if acl_check(bbs_num_str, 'bbs_view') == 1:
                     return re_error('/ban')
                     
+                text = ''
+                comment_num = ''
+                data_preview = ''
                 if do_type == 'preview':
                     text = flask.request.form.get('content', '')
                     text = text.replace('\r', '')
 
+                    comment_num = flask.request.form.get('comment_select', '')
+
                     data_preview = render_set(
-                        doc_name = '', 
                         doc_data = text,
-                        data_in = 'from'
+                        data_in = 'bbs_comment_preview'
                     )
-                else:
-                    text = ''
-                    data_preview = ''
-
-                temp_id = ''
-                temp_dict = {}
-
-                db_data = list(db_data) if db_data else []
-                for for_a in db_data + [['', '', '']]:
-                    if temp_id != for_a[2]:
-                        temp_id = for_a[2]
-                        temp_dict['code'] = for_a[2]
 
-                    temp_dict[for_a[0]] = for_a[1]
+                date = ''
+                date += '<a href="javascript:opennamu_change_comment(\'0\');">(' + load_lang('comment') + ')</a> '
+                date += '<a href="/bbs/w/' + bbs_num_str + '/' + post_num_str + '/tool">(' + load_lang('tool') + ')</a> '
+                date += temp_dict['date']
 
                 data = ''
                 data += '<h2>' + html.escape(temp_dict['title']) + '</h2>'
-                data += bbs_w_post_make_thread(
+                data += api_topic_thread_make(
                     ip_pas(temp_dict['user_id']),
-                    temp_dict['date'],
+                    date,
                     render_set(
-                        doc_name = '', 
                         doc_data = temp_dict['data'],
-                        data_in = 'from'
+                        data_in = 'bbs'
                     ),
                     '0',
                     color = 'red'
                 )
 
                 user_id = temp_dict['user_id']
-                temp_id = ''
-                temp_dict = {}
                 comment_data = ''
 
-                comment_select = '<hr class="main_hr"><select name="comment_select">'
-                comment_select += '<option value="default">' + load_lang('normal') + '</option>'
-
-                curs.execute(db_change('select set_name, set_data, set_code, set_id from bbs_data where (set_name = "comment" or set_name = "comment_date" or set_name = "comment_user_id") and set_id = ? order by set_code + 0 asc'), [bbs_num_str + '-' + post_num_str])
-                db_data = curs.fetchall()
-                if db_data:
-                    data += '<hr class="main_hr"><hr>'
-                else:
-                    db_data = []
+                comment_select = '<select id="opennamu_comment_select" name="comment_select">'
+                comment_select += '<option value="0">' + load_lang('normal') + '</option>'
 
-                for_a = 0
-                db_data_2 = db_data + [['', '', '', '']]
-                db_data_len = len(db_data_2)
                 comment_count = 0
                 comment_add_count = 0
-                while(for_a < db_data_len):
-                    if temp_id != (db_data_2[for_a][3] + '-' + db_data_2[for_a][2]):
-                        if temp_id != '':
-                            temp_dict['code'] = temp_id
-                            temp_dict['code'] = re.sub(r'^[0-9]+-[0-9]+-', '', temp_dict['code'])
-
-                            if user_id == temp_dict['comment_user_id']:
-                                color = 'green'
-                            else:
-                                color = 'default'
-
-                            margin_count = temp_dict['code'].count('-')
-                            if margin_count == 0:
-                                comment_count += 1
-                            else:
-                                comment_add_count += 1
-
-                            comment_data += '<span style="padding-left: 20px;"></span>' * margin_count
-                            comment_data += bbs_w_post_make_thread(
-                                ip_pas(temp_dict['comment_user_id']),
-                                temp_dict['comment_date'],
-                                render_set(
-                                    doc_name = '', 
-                                    doc_data = temp_dict['comment'],
-                                    data_in = 'from'
-                                ),
-                                temp_dict['code'],
-                                color = color,
-                                add_style = 'width: calc(100% - ' + str(margin_count * 20) + 'px);'
-                            )
-
-                            comment_select += '<option value="' + temp_dict['code'] + '">' + temp_dict['code'] + '</option>'
-
-                            curs.execute(db_change('select set_name, set_data, set_code, set_id from bbs_data where (set_name = "comment" or set_name = "comment_date" or set_name = "comment_user_id") and set_id = ? order by set_code + 0 asc'), [bbs_num_str + '-' + post_num_str + '-' + temp_dict['code']])
-                            db_data = curs.fetchall()
-                            if db_data:
-                                db_data_2 = db_data_2[:for_a] + db_data + db_data_2[for_a:]
-                                db_data_len += len(db_data)
-
-                            if db_data_2[for_a][0] != '':
-                                comment_data += '<hr class="main_hr">'
-
-                        temp_id = db_data_2[for_a][3] + '-' + db_data_2[for_a][2]
-
-                    temp_dict[db_data_2[for_a][0]] = db_data_2[for_a][1]
-                    for_a += 1
+
+                temp_data = bbs_w_post_comment(user_id, bbs_num_str + '-' + post_num_str, comment_num, bbs_num_str, post_num_str)
+
+                comment_data += temp_data[0]
+                comment_select += temp_data[1]
+                comment_count += temp_data[2]
+                comment_add_count += temp_data[3]
+                comment_add_count -= comment_count
+
+                if comment_data != '':
+                    data += '<hr class="main_hr"><hr>'
 
                 comment_select += '</select>'
                 if comment_data != '':
                     data += load_lang('comment') + ' : ' + str(comment_count) + '<hr class="main_hr">'
                     data += load_lang('reply') + ' : ' + str(comment_add_count) + '<hr class="main_hr">'
                     data += comment_data
+                else:
+                    data += '<hr class="main_hr">'
 
                 bbs_comment_form = ''
                 if bbs_comment_acl == 0:
-                    bbs_comment_form = '''
-                        ''' + comment_select + '''
+                    bbs_comment_form += '''
+                        ''' + comment_select + ''' <a href="javascript:opennamu_return_comment();">(R)</a>
                         <hr class="main_hr">
                         
-                        <textarea name="content" id="opennamu_edit_textarea" class="opennamu_textarea_100">''' + html.escape(text) + '''</textarea>
+                        ''' + edit_editor(curs, ip, text, 'thread') + '''
                         <hr class="main_hr">
                         
                         ''' + captcha_get() + ip_warning() + '''
 
-                        <button id="opennamu_save_button" formaction="/bbs/w/''' + bbs_num_str + '''/''' + post_num_str + '''" type="submit">''' + load_lang('send') + '''</button>
-                        <button id="opennamu_preview_button" formaction="/bbs/w/preview/''' + bbs_num_str + '''/''' + post_num_str + '''#opennamu_edit_textarea" type="submit">''' + load_lang('preview') + '''</button>
+                        <button id="opennamu_save_button" formaction="/bbs/w/''' + bbs_num_str + '''/''' + post_num_str + '''" type="submit" onclick="do_monaco_to_textarea(); do_stop_exit_release();">''' + load_lang('send') + '''</button>
+                        <button id="opennamu_preview_button" formaction="/bbs/w/preview/''' + bbs_num_str + '''/''' + post_num_str + '''#opennamu_edit_textarea" type="submit" onclick="do_monaco_to_textarea(); do_stop_exit_release();">''' + load_lang('preview') + '''</button>
                         <hr class="main_hr">
                     '''
 
@@ -370,10 +358,11 @@ def bbs_w_post(bbs_num = '', post_num = '', do_type = ''):
                         ''' + bbs_comment_form + '''
                         ''' + data_preview + '''
                     </form>
+                    <script src="/views/main_css/js/route/bbs_w_post.js"></script>
                 '''
 
                 return easy_minify(flask.render_template(skin_check(),
-                    imp = [load_lang('bbs_main'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
+                    imp = [bbs_name, wiki_set(), wiki_custom(), wiki_css(['(' + load_lang('bbs') + ')', 0])],
                     data = data,
                     menu = [['bbs/w/' + bbs_num_str, load_lang('return')], ['bbs/edit/' + bbs_num_str + '/' + post_num_str, load_lang('edit')]]
                 ))

+ 24 - 26
route/bbs_w_set.py

@@ -4,26 +4,24 @@ def bbs_w_set(bbs_num = ''):
     with get_db_connect() as conn:
         curs = conn.cursor()
 
-        curs.execute(db_change('select set_id from bbs_set where set_id = ? and set_name = "bbs_name"'), [bbs_num])
-        if not curs.fetchall():
+        curs.execute(db_change('select set_data from bbs_set where set_id = ? and set_name = "bbs_name"'), [bbs_num])
+        db_data = curs.fetchall()
+        if not db_data:
             return redirect('/bbs/main')
+        
+        bbs_name = db_data[0][0]
 
-        i_list = {
-            1 : 'bbs_acl',
-            2 : 'bbs_edit_acl',
-            3 : 'bbs_comment_acl',
-            4 : 'bbs_view_acl'
-        }
+        i_list = ['bbs_acl', 'bbs_edit_acl', 'bbs_comment_acl', 'bbs_view_acl', 'bbs_markup']
         bbs_num_str = str(bbs_num)
 
         if flask.request.method == 'POST':
             if admin_check(None, 'bbs_set (acl)') != 1:
                 return re_error('/ban')
             else:
-                for i in i_list:
+                for for_a in range(len(i_list)):
                     curs.execute(db_change("update bbs_set set set_data = ? where set_name = ? and set_id = ?"), [
-                        flask.request.form.get(i_list[i], 'normal'),
-                        i_list[i],
+                        flask.request.form.get(i_list[for_a], 'normal'),
+                        i_list[for_a],
                         bbs_num
                     ])
 
@@ -31,40 +29,40 @@ def bbs_w_set(bbs_num = ''):
 
                 return redirect('/bbs/set/' + bbs_num_str)
         else:
-            d_list = {}
+            d_list = ['' for _ in range(0, len(i_list))]
 
             if admin_check() != 1:
                 disable = 'disabled'
             else:
                 disable = ''
 
-            for i in i_list:
-                curs.execute(db_change('select set_data from bbs_set where set_name = ? and set_id = ?'), [i_list[i], bbs_num])
+            for for_a in range(len(i_list)):
+                curs.execute(db_change('select set_data from bbs_set where set_name = ? and set_id = ?'), [i_list[for_a], bbs_num])
                 sql_d = curs.fetchall()
                 if sql_d:
-                    d_list[i] = sql_d[0][0]
+                    d_list[for_a] = sql_d[0][0]
                 else:
-                    curs.execute(db_change('insert into bbs_set (set_name, set_code, set_id, set_data) values (?, "", ?, ?)'), [i_list[i], bbs_num, 'normal'])
-                    d_list[i] = 'normal'
+                    curs.execute(db_change('insert into bbs_set (set_name, set_code, set_id, set_data) values (?, "", ?, ?)'), [i_list[for_a], bbs_num, 'normal'])
+                    d_list[for_a] = 'normal'
 
             conn.commit()
 
-            acl_div = []
-            for i in range(0, len(i_list)):
-                acl_div += ['']
-
+            acl_div = ['' for _ in range(0, len(i_list))]
             acl_list = get_acl_list()
-            for i in range(0, len(i_list)):
+            for for_a in range(0, len(i_list)):
+                if for_a == 4:
+                    acl_list = ['normal'] + get_init_set_list('markup')['list']
+
                 for data_list in acl_list:
-                    if data_list == d_list[i + 1]:
+                    if data_list == d_list[for_a]:
                         check = 'selected="selected"'
                     else:
                         check = ''
 
-                    acl_div[i] += '<option value="' + data_list + '" ' + check + '>' + (data_list if data_list != '' else 'normal') + '</option>'
+                    acl_div[for_a] += '<option value="' + data_list + '" ' + check + '>' + (data_list if data_list != '' else 'normal') + '</option>'
 
             return easy_minify(flask.render_template(skin_check(),
-                imp = [load_lang('main_acl_setting'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
+                imp = [load_lang('bbs_set'), wiki_set(), wiki_custom(), wiki_css(['(' + bbs_name + ')', 0])],
                 data = render_simple_set('''
                     <form method="post">
                         <hr class="main_hr">
@@ -84,7 +82,7 @@ def bbs_w_set(bbs_num = ''):
                         <select ''' + disable + ''' name="bbs_comment_acl">''' + acl_div[2] + '''</select>
 
                         <h2>''' + load_lang('markup') + '''</h2>
-                        ''' + load_lang('not_working') + '''
+                        <select ''' + disable + ''' name="bbs_markup">''' + acl_div[4] + '''</select>
                         
                         <hr class="main_hr">
                         <button id="opennamu_save_button" type="submit">''' + load_lang('save') + '''</button>

+ 91 - 54
route/edit.py

@@ -1,5 +1,79 @@
+import multiprocessing
+
 from .tool.func import *
 
+
+def edit_render_set(name, content):
+    render_set(
+        doc_name = name,
+        doc_data = content
+    )
+
+# https://stackoverflow.com/questions/13821156/timeout-function-using-threading-in-python-does-not-work
+def edit_timeout(func, args = (), timeout = 3):
+    pool = multiprocessing.Pool(processes = 1)
+    result = pool.apply_async(func, args = args)
+    try:
+        result.get(timeout = timeout)
+    except multiprocessing.TimeoutError:
+        pool.terminate()
+        return 1
+    else:
+        pool.close()
+        pool.join()
+        return 0
+        
+def edit_editor(curs, ip, data_main = '', do_type = 'edit'):
+    monaco_editor_top = ''
+    editor_display = ''
+    add_get_file = ''
+    monaco_display = ''
+
+    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')
+    
+    monaco_on = get_main_skin_set(curs, flask.session, 'main_css_monaco', ip)
+    if monaco_on == 'use':
+        editor_display = 'style="display: none;"'
+        add_get_file = '''
+            <link   rel="stylesheet"
+                    data-name="vs/editor/editor.main" 
+                    href="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.37.1/min/vs/editor/editor.main.min.css">
+            <script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.37.1/min/vs/loader.min.js"></script>
+        '''
+
+        monaco_editor_top = '<a href="javascript:opennamu_edit_turn_off_monaco();">(' + load_lang('turn_off_monaco') + ')</a>'
+        
+        if flask.request.cookies.get('main_css_darkmode', '0') == '1':
+            monaco_thema = 'vs-dark'
+        else:
+            monaco_thema = ''
+        
+        add_script = 'do_monaco_init("' + monaco_thema + '");'
+    else:
+        monaco_display = 'style="display: none;"'
+        add_script = 'opennamu_edit_turn_off_monaco();'
+
+    if do_type == 'edit':
+        textarea_size = 'opennamu_textarea_500'
+    else:
+        textarea_size = 'opennamu_textarea_100'
+
+    return add_get_file + '''
+        <textarea style="display: none;" id="opennamu_edit_origin" name="doc_data_org">''' + html.escape(data_main) + '''</textarea>
+        <div>''' + monaco_editor_top + ' ' + edit_button('opennamu_edit_textarea', 'opennamu_monaco_editor') + '''</div>
+        
+        <div id="opennamu_monaco_editor" class="''' + textarea_size + '''" ''' + monaco_display + '''></div>
+        <textarea id="opennamu_edit_textarea" ''' + editor_display + ''' class="''' + textarea_size + '''" name="content" placeholder="''' + p_text + '''">''' + html.escape(data_main) + '''</textarea>
+        <hr class="main_hr">
+        <script>
+            do_stop_exit();
+            do_paste_image('opennamu_edit_textarea', 'opennamu_monaco_editor');
+            ''' + add_script + '''
+        </script>
+    '''
+
 def edit(name = 'Test', section = 0, do_type = ''):
     with get_db_connect() as conn:
         curs = conn.cursor()
@@ -74,12 +148,18 @@ def edit(name = 'Test', section = 0, do_type = ''):
             else:
                 leng = '+' + str(len(content))
 
-            render_set(
-                doc_name = name,
-                doc_data = content,
-                data_in = ''
-            )
-                
+            curs.execute(db_change("select data from other where name = 'edit_timeout'"))
+            db_data_2 = curs.fetchall()
+            db_data_2 = '' if not db_data_2 else number_check(db_data_2[0][0])
+
+            if db_data_2 != '' and platform.system() == 'Linux':
+                timeout = edit_timeout(edit_render_set, (name, content), timeout = int(db_data_2))
+            else:
+                timeout = 0
+
+            if timeout == 1:
+                return re_error('/error/41')
+            
             if db_data:
                 curs.execute(db_change("update data set data = ? where title = ?"), [content, name])
             else:    
@@ -90,7 +170,7 @@ def edit(name = 'Test', section = 0, do_type = ''):
     
             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) + '">' + html.escape(name) + '</a> | Edit')
+                add_alarm(scan_user[0], ip, '<a href="/w/' + url_pas(name) + '">' + html.escape(name) + '</a>')
                     
             history_plus(
                 name,
@@ -209,7 +289,7 @@ def edit(name = 'Test', section = 0, do_type = ''):
                     data_preview = render_set(
                         doc_name = name, 
                         doc_data = data,
-                        data_in = 'from'
+                        data_type = 'from'
                     )
 
             if data_section == '':
@@ -224,58 +304,21 @@ def edit(name = 'Test', section = 0, do_type = ''):
     
             editor_top_text += '<a href="/edit_filter">(' + load_lang('edit_filter_rule') + ')</a>'
     
-            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')
-            
-            monaco_on = get_main_skin_set(curs, flask.session, 'main_css_monaco', ip)
-            if monaco_on == 'use':
-                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.37.1/min/vs/editor/editor.main.min.css">
-                    <script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.37.1/min/vs/loader.min.js"></script>
-                '''
-
-                editor_top_text += ' <a href="javascript:opennamu_edit_turn_off_monaco();">(' + load_lang('turn_off_monaco') + ')</a>'
-                
-                if flask.request.cookies.get('main_css_darkmode', '0') == '1':
-                    monaco_thema = 'vs-dark'
-                else:
-                    monaco_thema = ''
-                
-                add_script = 'do_monaco_init("' + monaco_thema + '");'
-            else:
-                editor_display = ''
-                monaco_display = 'style="display: none;"'
-                add_get_file = ''
-                add_script = 'opennamu_edit_turn_off_monaco();'
-
             if editor_top_text != '':
                 editor_top_text += '<hr class="main_hr">'
 
             sub_menu = ' (' + str(section) + ')' if section != '' else ''
-    
+
             return easy_minify(flask.render_template(skin_check(), 
                 imp = [name, wiki_set(), wiki_custom(), wiki_css(['(' + load_lang('edit') + ')' + sub_menu, 0])],
-                data =  editor_top_text + add_get_file + '''
-                    <script>
-                        
-                    </script>
+                data = editor_top_text + '''
                     <form method="post">
-                        <textarea style="display: none;" id="opennamu_edit_origin" name="doc_data_org">''' + html.escape(data_section) + '''</textarea>
                         <textarea style="display: none;" name="doc_section_data_where">''' + data_section_where + '''</textarea>
                         <input style="display: none;" name="doc_section_edit_apply" value="''' + doc_section_edit_apply + '''">
 
                         <input style="display: none;" name="ver" value="''' + doc_ver + '''">
                         
-                        <div>''' + edit_button('opennamu_edit_textarea', 'opennamu_monaco_editor') + '''</div>
-                        
-                        <div id="opennamu_monaco_editor" class="opennamu_textarea_500" ''' + monaco_display + '''></div>
-                        <textarea id="opennamu_edit_textarea" ''' + editor_display + ''' class="opennamu_textarea_500" name="content" placeholder="''' + p_text + '''">''' + html.escape(data_section) + '''</textarea>
-                        <hr class="main_hr">
+                        ''' + edit_editor(curs, ip, data_section) + '''
 
                         <input placeholder="''' + load_lang('why') + '''" name="send">
                         <hr class="main_hr">
@@ -288,12 +331,6 @@ def edit(name = 'Test', section = 0, do_type = ''):
                     
                     <hr class="main_hr">
                     <div id="opennamu_preview_area">''' + data_preview + '''</div>
-                    
-                    <script>
-                        do_stop_exit();
-                        do_paste_image('opennamu_edit_textarea', 'opennamu_monaco_editor');
-                        ''' + add_script + '''
-                    </script>
                 ''',
                 menu = [
                     ['w/' + url_pas(name), load_lang('return')],

+ 10 - 12
route/edit_delete_file.py

@@ -1,27 +1,25 @@
 from .tool.func import *
+
 from .edit_delete import edit_delete
 
-# 처음으로 차세대 코드 방법론 적용
-# 앞으로 다 이렇게 작성할 예정
-def edit_delete_file(name : str = 'test.jpg') -> str:
+def edit_delete_file(name = 'test.jpg'):
     with get_db_connect() as conn:
-        curs : typing.Union[sqlite3.dbapi2.Cursor, pymysql.cursors.Cursor, None] = conn.cursor()
+        curs = conn.cursor()
 
-        ip : str = ip_check()
+        ip = ip_check()
         if admin_check() == 0:
             return re_error('/ban')
 
-        mime_type : typing.Union[re.Match, None] = re.search(r'([^.]+)$', name)
+        mime_type = re.search(r'([^.]+)$', name)
+        mime_type_str = 'jpg'
         if mime_type:
-            mime_type = mime_type.group(1).lower()
-        else:
-            mime_type = 'jpg'
+            mime_type_str = mime_type.group(1).lower()
 
-        file_name : str = re.sub(r'\.([^.]+)$', '', name)
+        file_name = re.sub(r'\.([^.]+)$', '', name)
         file_name = re.sub(r'^file:', '', file_name)
 
-        file_all_name : str = sha224_replace(file_name) + '.' + mime_type
-        file_directory : str = os.path.join(load_image_url(), file_all_name)
+        file_all_name = sha224_replace(file_name) + '.' + mime_type_str
+        file_directory = os.path.join(load_image_url(), file_all_name)
 
         if not os.path.exists(file_directory):
             return redirect('/w/' + url_pas(name))

+ 1 - 0
route/edit_delete_multiple.py

@@ -1,4 +1,5 @@
 from .tool.func import *
+
 from .edit_delete import edit_delete
 
 def edit_delete_multiple():

+ 1 - 1
route/give_admin_groups.py

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

+ 1 - 1
route/give_delete_admin_group.py

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

+ 3 - 4
route/list_image_file.py

@@ -1,11 +1,10 @@
 from .tool.func import *
 
-def list_image_file():
+def list_image_file(arg_num = 1):
     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
+        sql_num = (arg_num * 50 - 50) if arg_num * 50 > 0 else 0
 
         list_data = '<ul class="opennamu_ul">'
 
@@ -14,7 +13,7 @@ def list_image_file():
         for data in data_list:
             list_data += '<li><a href="/w/' + url_pas(data[0]) + '">' + html.escape(data[0]) + '</a></li>'
 
-        list_data += next_fix('/list/file/', num, data_list)
+        list_data += next_fix('/list/file/', arg_num, data_list)
 
         return easy_minify(flask.render_template(skin_check(),
             imp = [load_lang('image_file_list'), wiki_set(), wiki_custom(), wiki_css([0, 0])],

+ 8 - 5
route/list_long_page.py

@@ -1,9 +1,11 @@
 from .tool.func import *
 
-def list_long_page(tool = 'long_page'):
+def list_long_page(tool = 'long_page', arg_num = 1):
     with get_db_connect() as conn:
         curs = conn.cursor()
 
+        sql_num = (arg_num * 50 - 50) if arg_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')
@@ -12,11 +14,12 @@ def list_long_page(tool = 'long_page'):
         select_data = 'desc' if tool == 'long_page' else 'asc'
         title = 'long_page' if tool == 'long_page' else 'short_page'
 
-        curs.execute(db_change("select title, length(data) from data order by length(data) " + select_data + " limit 50"))
-        for data in curs.fetchall():
-            div += '<li>' + str(data[1]) + ' : <a href="/w/' + url_pas(data[0]) + '">' + html.escape(data[0]) + '</a></li>'
+        curs.execute(db_change("select title, length(data) from data order by length(data) " + select_data + " limit ?, 50"), [sql_num])
+        db_data = curs.fetchall()
+        for data in db_data:
+            div += '<li>' + str(data[1]) + ' | <a href="/w/' + url_pas(data[0]) + '">' + html.escape(data[0]) + '</a></li>'
 
-        div += '</ul>'
+        div += '</ul>' + next_fix('/list/document/' + ('long' if title == 'long_page' else 'short') + '/', arg_num, db_data)
 
         return easy_minify(flask.render_template(skin_check(),
             imp = [load_lang(title), wiki_set(), wiki_custom(), wiki_css([0, 0])],

+ 2 - 0
route/list_old_page.py

@@ -4,6 +4,8 @@ def list_old_page(num = 1):
     with get_db_connect() as conn:
         curs = conn.cursor()
         
+        # 리다이렉트 구분도 넣을 예정
+        # 그 전에 로직 개편하고
         sql_num = (num * 50 - 50) if num * 50 > 0 else 0
         
         curs.execute(db_change('select data from other where name = "count_all_title"'))

+ 40 - 27
route/give_user_check.py → route/list_user_check.py

@@ -1,20 +1,23 @@
 from .tool.func import *
 
-def give_user_check_2(name):
+def list_user_check(name = 'test', plus_name = None, arg_num = 1, do_type = 'normal'):
+    # 파라미터 to URL
     with get_db_connect() as conn:
         curs = conn.cursor()
 
-        plus_id = flask.request.args.get('plus', None)
+        plus_id = plus_name
+
+        check_type = do_type if do_type in ['simple', 'normal'] else 'normal'
+        check_type = '' if check_type == 'normal' else check_type
+
+        num = arg_num
+        sql_num = (num * 50 - 50) if num * 50 > 0 else 0
 
         if admin_check('all', None, name) == 1 or (plus_id and admin_check('all', None, plus_id) == 1):
             if admin_check() != 1:
                 return re_error('/error/4')
 
-        num = int(number_check(flask.request.args.get('num', '1')))
-        sql_num = (num * 50 - 50) if num * 50 > 0 else 0
-
         div = ''
-        check_type = flask.request.args.get('type', '')
 
         if admin_check(4, (check_type + ' ' if check_type != '' else '') + 'check (' + name + ')') != 1:
             return re_error('/error/3')
@@ -81,14 +84,14 @@ def give_user_check_2(name):
             if record:
                 if not plus_id:
                     div = '' + \
-                        '<a href="/manager/14?plus=' + url_pas(name) + '">(' + load_lang('compare') + ')</a> ' + \
-                        '<a href="/check/' + url_pas(name) + '?type=simple">(' + load_lang('simple_check') + ')</a>' + \
+                        '<a href="/manager/14/' + url_pas(name) + '">(' + load_lang('compare') + ')</a> ' + \
+                        '<a href="/list/user/check/' + url_pas(name) + '/simple">(' + load_lang('simple_check') + ')</a>' + \
                         '<hr class="main_hr">' + \
                     '' + div
                 else:
                     div = '' + \
-                        '<a href="/check/' + url_pas(name) + '">(' + name + ')</a> ' + \
-                        '<a href="/check/' + url_pas(plus_id) + '">(' + plus_id + ')</a>' + \
+                        '<a href="/list/user/check/' + url_pas(name) + '">(' + name + ')</a> ' + \
+                        '<a href="/list/user/check/' + url_pas(plus_id) + '">(' + plus_id + ')</a>' + \
                         '<hr class="main_hr">' + \
                     '' + div
 
@@ -119,16 +122,12 @@ def give_user_check_2(name):
                     div += '''
                         <tr>
                             <td>
-                                <a href="/check/''' + url_pas(data[0]) + '''">''' + data[0] + '''</a>
-                                <a  href="/check_delete''' + \
-                                    '''?name=''' + url_pas(data[0]) + \
-                                    '''&ip=''' + url_pas(data[1]) + \
-                                    '''&time=''' + url_pas(data[3].replace(' ', '').replace(':', '').replace('-', '')) + \
-                                    '''&return_type=''' + ('0' if ip_or_user(name) == 0 else '1') + '''">
+                                <a href="/list/user/check/''' + url_pas(data[0]) + '''">''' + data[0] + '''</a>
+                                <a href="/list/user/check/delete/''' + url_pas(data[0]) + '/' + url_pas(data[1]) + '/' + url_pas(data[3]) + '/' + ('0' if ip_or_user(name) == 0 else '1') + '''">
                                     (''' + load_lang('delete') + ''')
                                 </a>
                             </td>
-                            <td><a href="/check/''' + url_pas(data[1]) + '''">''' + data[1] + '''</a></td>
+                            <td><a href="/list/user/check/''' + url_pas(data[1]) + '''">''' + data[1] + '''</a></td>
                             <td>''' + data[3] + '''</td>
                         </tr>
                         <tr>
@@ -141,37 +140,51 @@ def give_user_check_2(name):
                     </table>
                 '''
 
-            div += next_fix(
-                '/check/' + url_pas(name) + ('?plus=' + plus_id + '&num=' if plus_id else '?num='), 
-                num, 
-                record
-            )
+            if plus_id:
+                div += get_next_page_bottom(
+                    '/list/user/check/' + url_pas(name) + '/normal/{}/' + url_pas(plus_id), 
+                    num, 
+                    record
+                )
+            else:
+                div += next_fix(
+                    '/list/user/check/' + url_pas(name) + '/normal/', 
+                    num, 
+                    record
+                )
+
+            if plus_id:
+                name += ', ' + plus_id
 
             return easy_minify(flask.render_template(skin_check(),
-                imp = [load_lang('check'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
+                imp = [name, wiki_set(), wiki_custom(), wiki_css(['(' + load_lang('check') + ')', 0])],
                 data = div,
                 menu = [['manager', load_lang('return')]]
             ))
         else:
             curs.execute(db_change("" + \
-                "select distinct " + ('name' if ip_or_user(name) == 1 else 'ip') + ", today from ua_d " + \
+                "select distinct " + ('name' if ip_or_user(name) == 1 else 'ip') + " from ua_d " + \
                 "where " + ('ip' if ip_or_user(name) == 1 else 'name') + " = ? "
                 "order by today desc limit ?, 50" + \
             ""), [name, sql_num])
             record = curs.fetchall()
 
             div = ''
-            for i in record:
-                div += '<li><a href="/check/' + url_pas(i[0]) + '?type=simple">' + i[0] + '</a></li>'
+            for for_a in record:
+                div += '<li><a href="/list/user/check/' + url_pas(for_a[0]) + '/simple">' + for_a[0] + '</a></li>'
 
             if div != '':
                 div = '<ul class="opennamu_ul">' + div + '</ul>'
                 div += next_fix(
-                    '/check/' + url_pas(name) + '?type=' + check_type + '&num=', 
+                    '/list/user/check/' + url_pas(name) + '/' + check_type + '/', 
                     num, 
                     record
                 )
 
+            div = '' + \
+                '<a href="/list/user/check/' + url_pas(name) + '/normal">(' + load_lang('check') + ')</a>' + \
+            '' + div
+
             return easy_minify(flask.render_template(skin_check(),
                 imp = [name, wiki_set(), wiki_custom(), wiki_css(['(' + load_lang('simple_check') + ')', 0])],
                 data = div,

+ 5 - 15
route/give_user_check_delete.py → route/list_user_check_delete.py

@@ -1,32 +1,22 @@
 from .tool.func import *
 
-def give_user_check_delete_2():
+def list_user_check_delete(name = None, ip = None, time = None, do_type = 1):
     with get_db_connect() as conn:
         curs = conn.cursor()
 
         if admin_check() != 1:
             return re_error('/error/4')
 
-        user_id = flask.request.args.get('name', None)
-        user_ip = flask.request.args.get('ip', None)
-
-        time = flask.request.args.get('time', None)
-        time_set = re.search(r'([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})', time)
-        if not time_set:
-            return redirect()
-
-        time_set = time_set.groups()
-        time = time_set[0] + '-' + time_set[1] + '-' + time_set[2] + ' '
-        time += time_set[3] + ':' + time_set[4] + ':' + time_set[5]
-
-        return_type = flask.request.args.get('return_type', '1')
+        user_id = name
+        user_ip = ip
+        return_type = do_type
 
         if user_id and user_ip and time:
             if flask.request.method == 'POST':
                 curs.execute(db_change("delete from ua_d where name = ? and ip = ? and today = ?"), [user_id, user_ip, time])
                 conn.commit()
 
-                return redirect('/check/' + url_pas(user_id if return_type == '0' else user_ip))
+                return redirect('/list/user/check/' + url_pas(user_id if return_type == '0' else user_ip))
             else:
                 return easy_minify(flask.render_template(skin_check(),
                     imp = [load_lang('check'), wiki_set(), wiki_custom(), wiki_css(['(' + load_lang('delete') + ')', 0])],

+ 19 - 5
route/login_login.py

@@ -12,10 +12,13 @@ def login_login_2():
             return re_error('/ban')
 
         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)
+            if 'login_count' in flask.session:
+                count = int(number_check(flask.session['login_count']))
+                if count > 3:
+                    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)
 
             user_agent = flask.request.headers.get('User-Agent', '')
             user_id = flask.request.form.get('id', '')
@@ -40,6 +43,11 @@ def login_login_2():
                 user_data['encode'],
                 user_id
             ) != 1:
+                if not 'login_count' in flask.session:
+                    flask.session['login_count'] = 1
+                else:
+                    flask.session['login_count'] = int(number_check(flask.session['login_count'])) + 1
+
                 return re_error('/error/10')
 
             curs.execute(db_change('select data from user_set where name = "2fa" and id = ?'), [user_id])
@@ -56,6 +64,12 @@ def login_login_2():
 
                 return redirect('/user')
         else:
+            captcha_data = ''
+            if 'login_count' in flask.session:
+                count = int(number_check(flask.session['login_count']))
+                if count > 3:
+                    captcha_data = captcha_get()
+
             return easy_minify(flask.render_template(skin_check(),
                 imp = [load_lang('login'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
                 data =  '''
@@ -66,7 +80,7 @@ def login_login_2():
                             <hr class="main_hr">
                             <!-- <input type="checkbox" name="auto_login"> ''' + load_lang('auto_login') + ''' (''' + load_lang('not_working') + ''')
                             <hr class="main_hr"> -->
-                            ''' + captcha_get() + '''
+                            ''' + captcha_data + '''
                             <button type="submit">''' + load_lang('login') + '''</button>
                             ''' + http_warning() + '''
                         </form>

+ 1 - 1
route/login_register_submit.py

@@ -44,7 +44,7 @@ def login_register_submit_2():
             conn.commit()
             
             for for_a in get_admin_list():
-                add_alarm(for_a, flask.session['submit_id'] + ' | <a href="/app_submit">' + load_lang('new_application') + '</a>')
+                add_alarm(for_a, flask.session['submit_id'], '<a href="/app_submit">' + load_lang('new_application') + '</a>')
 
             return redirect('/')
         else:

+ 2 - 23
route/main_func_easter_egg.py

@@ -3,23 +3,6 @@ from .tool.func import *
 def main_func_easter_egg():
     with get_db_connect() as conn:
         curs = conn.cursor()
-        
-        random_n = random.randrange(0, 8)
-        select_list = [
-            'PWD0ZbR7AOY', # TH06   - Shanghai Teahouse ~ Chinese Tea
-            'HoU29ljOmTE', # TH10.5 - Flawless Clothing of Celestials
-            'PR2vUm-Ald8', # TH06   - U.N. Owen Was Her
-            'opZoEmsu_Lo', # TH09   - Flowering Night
-            'txZFFTusSvw', # TH08   - Reach for the Moon ~ Immortal Smoke
-            'Ixq9xL2tvRU', # TH07   - Phantom Ensemble
-            '-3IAx_r4Au0', # TH17   - Entrusting This World to Idols ~ Idolatrize World
-            'wObZkycA6sc', # TH11   - Last Remote
-            'hZxYLa97gDg', # TH12   - Emotional Skyscraper ~ Cosmic Mind
-            'hwn2kw4eFJM', # TH07   - Border of Life
-            'wX2t_8HOtiY', # TH08   - Voyage 1969
-            'tLQjcf45fKE', # TH07   - Necrofantasia
-            # Remix by NyxTheShield
-        ]
 
         ip = ip_check()
         if ip_or_user(ip) == 0:
@@ -27,9 +10,5 @@ def main_func_easter_egg():
             if not curs.fetchall():
                 curs.execute(db_change('insert into user_set (name, id, data) values ("get_🥚", ?, "Y")'), [ip])
                 conn.commit()
-
-        return easy_minify(flask.render_template(skin_check(),
-            imp = ['Easter Egg', wiki_set(), wiki_custom(), wiki_css([0, 0])],
-            data = '<iframe width="640" height="360" src="https://www.youtube.com/embed/' + select_list[random_n] + '" frameborder="0" allowfullscreen></iframe>',
-            menu = [['manager', load_lang('return')]]
-        ))
+    
+        return redirect('/easter_egg_go')

+ 1 - 0
route/main_setting.py

@@ -13,6 +13,7 @@ def main_setting():
             ['sitemap', load_lang('sitemap_management')],
             ['top_menu', load_lang('top_menu_setting')],
             ['skin_set', load_lang('main_skin_set_default')],
+            ['acl', load_lang('main_acl_setting')]
         ]
 
         li_data = ''.join(['<li><a href="/setting/' + str(li[0]) + '">' + li[1] + '</a></li>' for li in li_list])

+ 8 - 5
route/main_setting_main.py

@@ -39,7 +39,8 @@ def main_setting_main(db_set):
             35 : ['user_name_view', ''],
             36 : ['link_case_insensitive', ''],
             37 : ['move_with_redirect', ''],
-            38 : ['slow_thread', '']
+            38 : ['slow_thread', ''],
+            39 : ['edit_timeout', '5'],
         }
 
         if flask.request.method == 'POST':
@@ -225,9 +226,6 @@ def main_setting_main(db_set):
                         </span>
 
                         <h2>''' + load_lang('edit_set') + '''</h2>
-                        <span><a href="/setting/acl">(''' + load_lang('main_acl_setting') + ''')</a></span>
-                        <hr class="main_hr">
-
                         <span>''' + load_lang('slow_edit') + ''' (''' + load_lang('second') + ''') (''' + load_lang('off') + ''' : ''' + load_lang('empty') + ''')</span>
                         <hr class="main_hr">
                         <input name="slow_edit" value="''' + html.escape(d_list[19]) + '''">
@@ -254,7 +252,7 @@ def main_setting_main(db_set):
                         <input type="checkbox" name="history_recording_off" ''' + check_box_div[9] + '''> ''' + load_lang('set_history_recording_off') + ''' (''' + load_lang('beta') + ''')
                         <hr class="main_hr">
 
-                        <input type="checkbox" name="move_with_redirect" ''' + check_box_div[13] + '''> ''' + load_lang('move_with_redirect') + '''
+                        <input type="checkbox" name="move_with_redirect" ''' + check_box_div[13] + '''> ''' + load_lang('move_with_redirect') + ''' (''' + load_lang('not_working') + ''')
                         <hr class="main_hr">
 
                         <span>''' + load_lang('slow_thread') + ''' (''' + load_lang('second') + ''') (''' + load_lang('off') + ''' : ''' + load_lang('empty') + ''') (''' + load_lang('not_working') + ''')</span>
@@ -262,6 +260,11 @@ def main_setting_main(db_set):
                         <input name="slow_thread" value="''' + html.escape(d_list[38]) + '''">
                         <hr class="main_hr">
 
+                        <span>''' + load_lang('edit_timeout') + ''' (''' + load_lang('second') + ''') (''' + load_lang('off') + ''' : ''' + load_lang('empty') + ''')</span>
+                        <hr class="main_hr">
+                        <input name="edit_timeout" value="''' + html.escape(d_list[39]) + '''">
+                        <hr class="main_hr">
+
                         <button id="opennamu_save_button" type="submit">''' + load_lang('save') + '''</button>
                     </form>
                 '''),

+ 5 - 1
route/main_setting_phrase.py

@@ -28,7 +28,8 @@ def main_setting_phrase():
             'topic_text',
             'phrase_user_page_admin',
             'phrase_user_page_owner',
-            'phrase_old_page_warring'
+            'phrase_old_page_warring',
+            'bbs_help'
         ]
         if flask.request.method == 'POST':
             for i in i_list:
@@ -124,6 +125,9 @@ def main_setting_phrase():
 
                         <h2>''' + load_lang('phrase_old_page_warring') + ''' (''' + load_lang('beta') + ''') (HTML)</h2>
                         <textarea class="opennamu_textarea_100" name="''' + i_list[20] + '''">''' + html.escape(d_list[20]) + '''</textarea>
+                        
+                        <h2>''' + load_lang('bbs_help') + '''</h2>
+                        <textarea class="opennamu_textarea_100" name="''' + i_list[13] + '''">''' + html.escape(d_list[22]) + '''</textarea>
 
                         <hr class="main_hr">
                         <button id="opennamu_save_button" type="submit">''' + load_lang('save') + '''</button>

+ 5 - 0
route/main_setting_skin_set.py

@@ -1,4 +1,5 @@
 from .tool.func import *
+
 from .user_setting_skin_set_main import user_setting_skin_set_main_set_list
 
 def main_setting_skin_set():
@@ -99,6 +100,10 @@ def main_setting_skin_set():
                         <select name="main_css_darkmode">
                             ''' + set_data["main_css_darkmode"] + '''
                         </select>
+                        <h3>''' + load_lang("table_scroll") + '''</h3>
+                        <select name="main_css_table_scroll">
+                            ''' + set_data["main_css_table_scroll"] + '''
+                        </select>
                         <h2>''' + load_lang("edit") + '''</h2>
                         <h3>''' + load_lang("image_paste") + '''</h3>
                         <sup>''' + load_lang('only_korean') + '''</sup> <sup>''' + load_lang('unavailable_in_monaco') + '''</sup>

+ 3 - 0
route/main_sys_update.py

@@ -1,3 +1,6 @@
+import zipfile
+import urllib.request
+
 from .tool.func import *
 
 def main_sys_update():

+ 7 - 4
route/main_tool_redirect.py

@@ -4,7 +4,7 @@ def main_tool_redirect(num = 1, add_2 = ''):
     with get_db_connect() as conn:
         title_list = {
             0 : [load_lang('document_name'), '/acl', load_lang('acl')],
-            1 : [0, '/check', load_lang('check')],
+            1 : [0, '/list/user/check', load_lang('check')],
             2 : [load_lang('file_name'), '/file_filter/add', load_lang('file_filter_add')],
             3 : [0, '/auth/give', load_lang('authorize')],
             4 : [0, '/record', load_lang('edit_record')],
@@ -15,7 +15,7 @@ def main_tool_redirect(num = 1, add_2 = ''):
             9 : [0, '/block_log/user', load_lang('blocked_user')],
             10 : [0, '/block_log/admin', load_lang('blocked_admin')],
             11 : [load_lang('document_name'), '/watch_list', load_lang('add_watchlist')],
-            12 : [load_lang('compare_target'), '/check', load_lang('compare_target')],
+            12 : [load_lang('compare_target'), '/list/user/check', load_lang('compare_target')],
             13 : [load_lang('document_name'), '/edit', load_lang('load')],
             14 : [load_lang('document_name'), '/star_doc', load_lang('add_star_doc')],
             15 : [load_lang('name_or_ip_or_regex'), '/auth/give/ban', load_lang('release')],
@@ -30,8 +30,11 @@ def main_tool_redirect(num = 1, add_2 = ''):
             add_1 = flask.request.form.get('name', 'test')
             if flask.request.method == 'POST':
                 if add_2 != '':
-                    flask.session['edit_load_document'] = add_1
-                    return redirect('/edit_from/' + url_pas(add_2))
+                    if num != 12:
+                        flask.session['edit_load_document'] = add_1
+                        return redirect('/edit_from/' + url_pas(add_2))
+                    else:
+                        return redirect(title_list[num][1] + '/' + url_pas(add_2) + '/normal/1/' + url_pas(add_1))
                 elif flask.request.form.get('regex', '') != '':
                     return redirect('/auth/give/ban_regex/' + url_pas(add_1))
                 else:

+ 1 - 1
route/recent_block.py

@@ -1,6 +1,6 @@
 from .tool.func import *
 
-def recent_block_2(name, tool):
+def recent_block_2(name = 'Test', tool = 'all'):
     with get_db_connect() as conn:
         curs = conn.cursor()
 

+ 1 - 2
route/recent_history_add.py

@@ -47,8 +47,7 @@ def recent_history_add(name = 'Test', do_type = ''):
 
                 data_preview = render_set(
                     doc_name = name, 
-                    doc_data = data,
-                    data_in = ''
+                    doc_data = data
                 )
             
             return easy_minify(flask.render_template(skin_check(),

ファイルの差分が大きいため隠しています
+ 468 - 481
route/tool/func.py


+ 37 - 9
route/tool/func_render.py

@@ -1,4 +1,5 @@
 from .func_tool import *
+
 from .func_render_namumark import class_do_render_namumark
 
 # 커스텀 마크 언젠간 다시 추가 예정
@@ -19,21 +20,24 @@ class class_do_render:
         curs = self.conn.cursor()
 
         doc_set = {}
-        if data_in == 'from':
-            data_in = ''
+        if data_type == 'from':
             doc_set['doc_from'] = 'O'
+            data_type = 'view'
         
         data_in = (data_in + '_') if data_in != '' else ''
         doc_set['doc_include'] = data_in
+        rep_data = ''
 
-        curs.execute(db_change("select set_data from data_set where doc_name = ? and set_name = 'document_markup'"), [doc_name])
-        rep_data = curs.fetchall()
-        if rep_data and rep_data[0][0] != '':
-            rep_data = rep_data[0][0]
-        else:
+        if rep_data == '' and doc_name != '':
+            curs.execute(db_change("select set_data from data_set where doc_name = ? and set_name = 'document_markup'"), [doc_name])
+            db_data = curs.fetchall()
+            if db_data and db_data[0][0] != '' and db_data[0][0] != 'normal':
+                rep_data = db_data[0][0]
+
+        if rep_data == '':
             curs.execute(db_change('select data from other where name = "markup"'))
-            rep_data = curs.fetchall()
-            rep_data = rep_data[0][0] if rep_data else 'namumark'
+            db_data = curs.fetchall()
+            rep_data = db_data[0][0] if db_data else 'namumark'
 
         if rep_data == 'namumark' or rep_data == 'namumark_beta':
             data_end = class_do_render_namumark(
@@ -56,6 +60,30 @@ class class_do_render:
                 {}
             ]
 
+        if data_type == 'thread' or data_type == 'api_thread':
+            def do_thread_a_change(match):
+                data = match[2].replace('#', '')
+                data_split = data.split('-')
+                if match[1] == 'topic_a' or len(data_split) == 1:
+                    return '<a href="' + match[2] + '">' + match[2] + '</a>'
+                elif match[1] == 'topic_a_post' and len(data_split) == 3:
+                    return '<a href="/bbs/w/' + data_split[2] + '/' + data_split[1] + '#' + data_split[0] + '">#' + data_split[0] + '-' + data_split[1] + '</a>'
+                elif len(data_split) == 2:
+                    return '<a href="/thread/' + data_split[1] + '#' + data_split[0] + '">' + match[2] + '</a>'
+                else:
+                    return ''
+
+            data_end[0] = re.sub(
+                r'&lt;(topic_a(?:_post|_thread)?)&gt;((?:(?!&lt;\/topic_a(?:_post|_thread)?&gt;).)+)&lt;\/topic_a(?:_post|_thread)?&gt;',
+                do_thread_a_change,
+                data_end[0]
+            )
+            data_end[0] = re.sub(
+                r'&lt;topic_call&gt;@(?P<in>(?:(?!&lt;\/topic_call&gt;).)+)&lt;\/topic_call&gt;',
+                '<a href="/w/user:\\g<in>">@\\g<in></a>',
+                data_end[0]
+            )
+
         if data_type == 'backlink':
             if 'backlink' in data_end[2]:
                 backlink = data_end[2]['backlink']

+ 230 - 39
route/tool/func_render_namumark.py

@@ -1,7 +1,7 @@
 from .func_tool import *
 
 class class_do_render_namumark:
-    def __init__(self, curs, doc_name, doc_data, doc_set, lang_data):
+    def __init__(self, curs, doc_name, doc_data, doc_set, lang_data, footnote = {}):
         self.curs = curs
         
         self.doc_data = doc_data.replace('\r', '')
@@ -30,7 +30,6 @@ class class_do_render_namumark:
         except:
             self.darkmode = '0'
 
-
         self.data_temp_storage = {}
         self.data_temp_storage_count = 0
 
@@ -42,6 +41,7 @@ class class_do_render_namumark:
         
         self.data_toc = ''
         self.data_footnote = {}
+        self.data_footnote_all = {}
         self.data_category = ''
         self.data_category_list = []
 
@@ -152,10 +152,7 @@ class class_do_render_namumark:
 
         return data
 
-    def get_tool_footnote_make(self):
-        footnote_number_set = get_main_skin_set(self.curs, self.flask_session, 'main_css_footnote_number', self.ip)
-        footnote_number_view_set = get_main_skin_set(self.curs, self.flask_session, 'main_css_view_real_footnote_num', self.ip)
-    
+    def get_tool_footnote_make(self):    
         data = ''
         for for_a in self.data_footnote:
             if data == '':
@@ -176,6 +173,7 @@ class class_do_render_namumark:
         if data != '':
             data += '</div>'
 
+        self.data_footnote_all.update(self.data_footnote)
         self.data_footnote = {}
 
         return data
@@ -316,14 +314,14 @@ class class_do_render_namumark:
         while 1:
             heading_count += 1
 
-            if not re.search(heading_regex, self.render_data):
+            heading_data = re.search(heading_regex, self.render_data)
+            if not heading_data:
                 break
             elif heading_count_all < 0:
                 print('Error : render heading count overflow')
 
                 break
             else:
-                heading_data = re.search(heading_regex, self.render_data)
                 heading_data_org = heading_data.group(0)
                 heading_data = heading_data.groups()
 
@@ -635,7 +633,7 @@ class class_do_render_namumark:
                 return '<macro>' + match[0] + '(' + match[1] + ')' + '</macro>'
 
         # double macro replace
-        self.render_data = re.sub(r'\[([^[(]+)\(([^()]+)\)\]', do_render_macro_double, self.render_data)
+        self.render_data = re.sub(r'\[([^[(\]]+)\(((?:(?!\)\]).)+)\)\]', do_render_macro_double, self.render_data)
 
         # single macro function
         def do_render_macro_single(match):
@@ -709,8 +707,8 @@ class class_do_render_namumark:
 
             return '<' + data_name + '></' + data_name + '>'
 
-        math_regex = re.compile('\[math\(((?:(?!\[math\(|\)\]).|\n)+)\)\]', re.I)
-        math_regex_2 = re.compile('&lt;math&gt;((?:(?!&lt;math&gt;|&lt;\/math&gt;).)+)&lt;\/math&gt;', re.I)
+        math_regex = re.compile(r'\[math\(((?:(?!\[math\(|\)\]).|\n)+)\)\]', re.I)
+        math_regex_2 = re.compile(r'&lt;math&gt;((?:(?!&lt;math&gt;|&lt;\/math&gt;).)+)&lt;\/math&gt;', re.I)
 
         self.render_data = re.sub(math_regex_2, do_render_math_sub, self.render_data)
         self.render_data = re.sub(math_regex, do_render_math_sub, self.render_data)
@@ -720,7 +718,8 @@ class class_do_render_namumark:
         image_count = 0
         link_count_all = len(re.findall(link_regex, self.render_data)) * 4
         while 1:
-            if not re.search(link_regex, self.render_data):
+            link_data = re.search(link_regex, self.render_data)
+            if not link_data:
                 break
             elif link_count_all < 0:
                 print('Error : render link count overflow')
@@ -728,7 +727,6 @@ class class_do_render_namumark:
                 break
             else:
                 # link split
-                link_data = re.search(link_regex, self.render_data)
                 link_data_full = link_data.group(0)
                 link_data = link_data.groups()
 
@@ -743,6 +741,7 @@ class class_do_render_namumark:
                     file_bgcolor = ''
                     file_turn = ''
                     file_radius = ''
+                    file_rendering = ''
 
                     file_split_regex = r'(?:^|&amp;) *((?:(?!&amp;).)+)'
                     file_split_sub_regex = r'(^[^=]+) *= *([^=]+)'
@@ -768,6 +767,9 @@ class class_do_render_namumark:
                                         file_turn = 'light'
                                 elif data_sub[0] == 'border-radius':
                                     file_radius = self.get_tool_px_add_check(data_sub[1])
+                                elif data_sub[0] == 'rendering':
+                                    if data_sub[1] == 'pixelated':
+                                        file_rendering = 'pixelated'
 
                     link_main_org = ''
                     link_sub = link_main
@@ -828,11 +830,16 @@ class class_do_render_namumark:
                     if file_radius != '':
                         file_radius = 'border-radius:' + self.get_tool_css_safe(file_radius) + ';'
 
+                    if file_rendering != '':
+                        file_rendering = 'image-rendering:' + self.get_tool_css_safe(file_rendering) + ';'
+
+                    file_style = file_width + file_height + file_align_style + file_bgcolor + file_radius + file_rendering
+
                     image_set = get_main_skin_set(self.curs, self.flask_session, 'main_css_image_set', self.ip)
                     if image_set == 'new_click' or image_set == 'click':
-                        file_end = '<img style="' + file_width + file_height + file_align_style + file_bgcolor + file_radius + '" id="opennamu_image_' + str(image_count) + '" alt="' + link_sub + '" src="">'
+                        file_end = '<img style="' + file_style + '" id="opennamu_image_' + str(image_count) + '" alt="' + link_sub + '" src="">'
                     else:
-                        file_end = '<img style="' + file_width + file_height + file_align_style + file_bgcolor + file_radius + '" alt="' + link_sub + '" src="' + link_main + '">'
+                        file_end = '<img style="' + file_style + '" alt="' + link_sub + '" src="' + link_main + '">'
 
                     if file_align == 'center':
                         file_end = '<div style="text-align:center;">' + file_end + '</div>'
@@ -978,8 +985,11 @@ class class_do_render_namumark:
                     link_main = self.get_tool_data_restore(link_main, do_type = 'slash')
                     link_title = link_main
                     link_main = html.unescape(link_main)
+
                     link_main = re.sub(r'"', '&quot;', link_main)
-                    
+                    link_main = re.sub(r'<', '&lt;', link_main)
+                    link_main = re.sub(r'>', '&gt;', link_main)
+
                     # sub not exist -> sub = main
                     if link_data[1]:
                         link_sub = link_data[1]
@@ -1114,7 +1124,7 @@ class class_do_render_namumark:
 
         include_num = 0
         include_set_data = get_main_skin_set(self.curs, self.flask_session, 'main_css_include_link', self.ip)
-        include_regex = re.compile('\[include\(((?:(?!\[include\(|\)\]|<\/div>).)+)\)\]', re.I)
+        include_regex = re.compile(r'\[include\(((?:(?!\[include\(|\)\]|<\/div>).)+)\)\]', re.I)
         include_count_max = len(re.findall(include_regex, self.render_data)) * 2
         include_change_list = {}
         while 1:
@@ -1146,9 +1156,10 @@ class class_do_render_namumark:
                             
                             data_sub_name = data_sub[0]
                             data_sub_data = self.get_tool_data_restore(data_sub[1], do_type = 'slash')
+                            data_sub_data = html.unescape(data_sub_data)
                             
-                            data_sub_data = re.sub(r'^(?P<in>분류|category):', ':\g<in>:', data_sub_data)
-                            data_sub_data = re.sub(r'^(?P<in>파일|file):', ':\g<in>:', data_sub_data)
+                            data_sub_data = re.sub(r'^(?P<in>분류|category):', ':\\g<in>:', data_sub_data)
+                            data_sub_data = re.sub(r'^(?P<in>파일|file):', ':\\g<in>:', data_sub_data)
 
                             include_change_list[data_sub_name] = data_sub_data
                         else:
@@ -1202,7 +1213,7 @@ class class_do_render_namumark:
         footnote_number_set = get_main_skin_set(self.curs, self.flask_session, 'main_css_footnote_number', self.ip)
         footnote_number_view_set = get_main_skin_set(self.curs, self.flask_session, 'main_css_view_real_footnote_num', self.ip)
 
-        footnote_regex = re.compile('(?:\[\*((?:(?!\[\*|\]| ).)+)?(?: ((?:(?!\[\*|\]).)+))?\]|\[(각주|footnote)\])', re.I)
+        footnote_regex = re.compile(r'(?:\[\*((?:(?!\[\*|\]| ).)+)?(?: ((?:(?!\[\*|\]).)+))?\]|\[(각주|footnote)\])', re.I)
         footnote_count_all = len(re.findall(footnote_regex, self.render_data)) * 4
         while 1:
             footnote_num += 1
@@ -1236,9 +1247,15 @@ class class_do_render_namumark:
                     rfn = ''
                     foot_v_name = ''
 
-                    if footnote_name in self.data_footnote:
-                        self.data_footnote[footnote_name]['list'] += [footnote_num_str]
-                        footnote_first = self.data_footnote[footnote_name]['list'][0]
+                    if footnote_name in self.data_footnote_all or footnote_name in self.data_footnote:
+                        if footnote_name in self.data_footnote:
+                            self.data_footnote[footnote_name]['list'] += [footnote_num_str]
+                            footnote_first = self.data_footnote[footnote_name]['list'][0]
+                        else:
+                            self.data_footnote[footnote_name] = {}
+                            self.data_footnote[footnote_name]['list'] = [footnote_num_str]
+                            self.data_footnote[footnote_name]['data'] = footnote_text_data
+                            footnote_first = self.data_footnote_all[footnote_name]['list'][0]
 
                         fn = self.doc_include + 'fn_' + footnote_first
                         rfn = self.doc_include + 'rfn_' + footnote_num_str
@@ -1457,7 +1474,7 @@ class class_do_render_namumark:
 
             return table_parameter_all
 
-        table_regex = re.compile('\n((?:(?:(?:(?:\|\|)+)|(?:\|[^|]+\|(?:\|\|)*))\n?(?:(?:(?!\|\|).)+))(?:(?:\|\||\|\|\n|(?:\|\|)+(?!\n)(?:(?:(?!\|\|).)+)\n*)*)\|\|)\n', re.DOTALL)
+        table_regex = re.compile(r'\n((?:(?:(?:(?:\|\|)+)|(?:\|[^|]+\|(?:\|\|)*))\n?(?:(?:(?!\|\|).)+))(?:(?:\|\||\|\|\n|(?:\|\|)+(?!\n)(?:(?:(?!\|\|).)+)\n*)*)\|\|)\n', re.DOTALL)
         table_sub_regex = r'(\n?)((?:\|\|)+)((?:&lt;(?:(?:(?!&lt;|&gt;).)+)&gt;)*)((?:\n*(?:(?:(?:(?!\|\|).)+)\n*)+)|(?:(?:(?!\|\|).)*))'
         table_caption_regex = r'^\|([^|]+)\|'
         table_count_all = len(re.findall(table_regex, self.render_data)) * 2
@@ -1532,7 +1549,7 @@ class class_do_render_namumark:
                 table_data_end = '<table class="' + table_parameter['class'] + '" style="' + table_parameter['table'] + '">' + table_caption + table_data_end + '</table>'
                 table_data_end = '<div class="table_safe" style="' + table_parameter['div'] + '">' + table_data_end + '</div>'
 
-                self.render_data = re.sub(table_regex, lambda x : ('\n<front_br>' + table_data_end + '<back_br>\n'), self.render_data, 1)
+                self.render_data = re.sub(table_regex, lambda x : ('\n<front_br>' + table_data_end + '\n'), self.render_data, 1)
 
             table_count_all -= 1
     
@@ -1660,7 +1677,11 @@ class class_do_render_namumark:
 
                         if syntax_count == 0:
                             self.render_data_js += 'hljs.highlightAll();\n'
-                            self.render_data_cdn += '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/default.min.css" integrity="sha512-hasIneQUHlh06VNBe7f6ZcHmeRTLIaQWFd43YriJ0UND19bvYRauxthDg8E4eVNPm9bRUhr5JGeqH7FRFXQu5g==" crossorigin="anonymous" referrerpolicy="no-referrer" />'
+                            if self.darkmode == '0':
+                                self.render_data_cdn += '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/default.min.css" integrity="sha512-hasIneQUHlh06VNBe7f6ZcHmeRTLIaQWFd43YriJ0UND19bvYRauxthDg8E4eVNPm9bRUhr5JGeqH7FRFXQu5g==" crossorigin="anonymous" referrerpolicy="no-referrer" />'
+                            else:
+                                self.render_data_cdn += '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/dark.min.css" integrity="sha512-bfLTSZK4qMP/TWeS1XJAR/VDX0Uhe84nN5YmpKk5x8lMkV0D+LwbuxaJMYTPIV13FzEv4CUOhHoc+xZBDgG9QA==" crossorigin="anonymous" referrerpolicy="no-referrer" />'
+
                             self.render_data_cdn += '<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js" integrity="sha512-rdhY3cbXURo13l/WU9VlaRyaIYeJ/KBakckXIvJNAQde8DgpOmE+eZf7ha4vdqVjTtwQt69bD2wH2LXob/LB7Q==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>'
                             self.render_data_cdn += '<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/x86asm.min.js" integrity="sha512-HeAchnWb+wLjUb2njWKqEXNTDlcd1QcyOVxb+Mc9X0bWY0U5yNHiY5hTRUt/0twG8NEZn60P3jttqBvla/i2gA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>'
 
@@ -1789,7 +1810,7 @@ class class_do_render_namumark:
 
             middle_count_all -= 1
 
-        self.render_data = re.sub(r'<temp_(?P<in>(?:slash)_(?:[0-9]+))>', '<\g<in>>', self.render_data)
+        self.render_data = re.sub(r'<temp_(?P<in>(?:slash)_(?:[0-9]+))>', '<\\g<in>>', self.render_data)
 
     def do_render_hr(self):
         hr_regex = r'\n-{4,9}\n'
@@ -1805,7 +1826,8 @@ class class_do_render_namumark:
 
             hr_count_max -= 1
 
-    def do_render_list(self):        
+    def do_render_list(self):
+        # 인용문
         quote_regex = r'((?:\n&gt; *[^\n]*)+)\n'
         quote_count = 0
         quote_count_max = len(re.findall(quote_regex, self.render_data)) * 10
@@ -1819,7 +1841,7 @@ class class_do_render_namumark:
                 quote_data_org = quote_data.group(0)
                 
                 quote_data = quote_data.group(1)
-                quote_data = re.sub(r'\n&gt; *(?P<in>[^\n]*)', '\g<in>\n', quote_data)
+                quote_data = re.sub(r'\n&gt; *(?P<in>[^\n]*)', '\\g<in>\n', quote_data)
                 quote_data = re.sub(r'\n$', '', quote_data)
                 quote_data = self.get_tool_data_revert(quote_data)
                 quote_data = html.unescape(quote_data)
@@ -1833,18 +1855,19 @@ class class_do_render_namumark:
             quote_count_max -= 1
             quote_count += 1
 
+        # 일반 리스트
+        list_style = {
+            1 : 'opennamu_list_1',
+            2 : 'opennamu_list_2',
+            3 : 'opennamu_list_3',
+            4 : 'opennamu_list_4'
+        }
         def do_render_list_sub(match):
             list_data = match.group(2)
             list_len = len(match.group(1))
             if list_len == 0:
                 list_len = 1
 
-            list_style = {
-                1 : 'opennamu_list_1',
-                2 : 'opennamu_list_2',
-                3 : 'opennamu_list_3',
-                4 : 'opennamu_list_4'
-            }
             list_style_data = 'opennamu_list_5'
             if list_len in list_style:
                 list_style_data = list_style[list_len]
@@ -1869,6 +1892,173 @@ class class_do_render_namumark:
 
             list_count_max -= 1
 
+        # 기타 리스트 공통 파트
+        def int_to_alpha(num):
+            alpha_list = string.ascii_lowercase
+            alpha_len = len(alpha_list)
+            end_text = ''
+
+            while num:
+                end_text = alpha_list[num % alpha_len - 1] + end_text
+                num = num // alpha_len
+
+            return end_text
+ 
+        # https://www.geeksforgeeks.org/python-program-to-convert-integer-to-roman/
+        def int_to_roman(number):
+            num = [1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000]
+            sym = ["I", "IV", "V", "IX", "X", "XL", "L", "XC", "C", "CD", "D", "CM", "M"]
+            i = 12
+            end_text = ''
+
+            while number:
+                div = number // num[i]
+                number %= num[i]
+        
+                while div:
+                    end_text += sym[i]
+                    div -= 1
+
+                i -= 1
+
+            return end_text
+        
+        class do_render_list_int_to:
+            def __init__(self, do_type):
+                self.list_num = []
+                self.do_type = do_type
+
+            def __call__(self, match):
+                list_data = match.group(3)
+                list_start = match.group(2)
+                list_len = len(match.group(1))
+                if list_len == 0:
+                    list_len = 1
+
+                if len(self.list_num) >= list_len:
+                    self.list_num[list_len - 1] += 1
+
+                    for for_a in range(list_len, len(self.list_num)):
+                        self.list_num[for_a] = 0
+                else:
+                    self.list_num += [1] * (list_len - len(self.list_num))
+
+                if list_start:
+                    self.list_num[list_len - 1] = int(list_start)
+
+                if self.do_type == 'int':
+                    change_text = str(self.list_num[list_len - 1])
+                elif self.do_type == 'roman_big':
+                    change_text = int_to_roman(self.list_num[list_len - 1])
+                elif self.do_type == 'roman_small':
+                    change_text = int_to_roman(self.list_num[list_len - 1]).lower()
+                elif self.do_type == 'alpha_big':
+                    change_text = int_to_alpha(self.list_num[list_len - 1])
+                else:
+                    change_text = int_to_alpha(self.list_num[list_len - 1]).upper()
+
+                return '<li style="margin-left: ' + str((list_len - 1) * 20) + 'px;" class="opennamu_list_none">' + change_text + '. ' + list_data + '</li>'
+
+        # 숫자 리스트
+        list_regex = r'((?:\n *1\. ?[^\n]*)+)\n'
+        list_count_max = len(re.findall(list_regex, self.render_data)) * 3
+        while 1:
+            list_data = re.search(list_regex, self.render_data)
+            if list_count_max < 0:
+                break
+            elif not list_data:
+                break
+            else:
+                list_data = list_data.group(1)
+                list_sub_regex = r'\n( *)1\.(?:#([0-9]*))? ?([^\n]*)'
+
+                list_class = do_render_list_int_to('int')
+                list_data = re.sub(list_sub_regex, list_class, list_data)
+
+                self.render_data = re.sub(list_regex, lambda x : ('\n<front_br><ul class="opennamu_ul">' + list_data + '</ul><back_br>\n'), self.render_data, 1)
+
+            list_count_max -= 1
+
+        # 소문자 리스트
+        list_regex = r'((?:\n *a\. ?[^\n]*)+)\n'
+        list_count_max = len(re.findall(list_regex, self.render_data)) * 3
+        while 1:
+            list_data = re.search(list_regex, self.render_data)
+            if list_count_max < 0:
+                break
+            elif not list_data:
+                break
+            else:
+                list_data = list_data.group(1)
+                list_sub_regex = r'\n( *)a.(?:#([0-9]*))? ?([^\n]*)'
+
+                list_class = do_render_list_int_to('alpha_small')
+                list_data = re.sub(list_sub_regex, list_class, list_data)
+
+                self.render_data = re.sub(list_regex, lambda x : ('\n<front_br><ul class="opennamu_ul">' + list_data + '</ul><back_br>\n'), self.render_data, 1)
+
+            list_count_max -= 1
+
+        # 대문자 리스트
+        list_regex = r'((?:\n *A\. ?[^\n]*)+)\n'
+        list_count_max = len(re.findall(list_regex, self.render_data)) * 3
+        while 1:
+            list_data = re.search(list_regex, self.render_data)
+            if list_count_max < 0:
+                break
+            elif not list_data:
+                break
+            else:
+                list_data = list_data.group(1)
+                list_sub_regex = r'\n( *)A.(?:#([0-9]*))? ?([^\n]*)'
+
+                list_class = do_render_list_int_to('alpha_big')
+                list_data = re.sub(list_sub_regex, list_class, list_data)
+
+                self.render_data = re.sub(list_regex, lambda x : ('\n<front_br><ul class="opennamu_ul">' + list_data + '</ul><back_br>\n'), self.render_data, 1)
+
+            list_count_max -= 1
+
+        # 로마자 대문자 리스트
+        list_regex = r'((?:\n *I\. ?[^\n]*)+)\n'
+        list_count_max = len(re.findall(list_regex, self.render_data)) * 3
+        while 1:
+            list_data = re.search(list_regex, self.render_data)
+            if list_count_max < 0:
+                break
+            elif not list_data:
+                break
+            else:
+                list_data = list_data.group(1)
+                list_sub_regex = r'\n( *)I.(?:#([0-9]*))? ?([^\n]*)'
+
+                list_class = do_render_list_int_to('roman_big')
+                list_data = re.sub(list_sub_regex, list_class, list_data)
+
+                self.render_data = re.sub(list_regex, lambda x : ('\n<front_br><ul class="opennamu_ul">' + list_data + '</ul><back_br>\n'), self.render_data, 1)
+
+            list_count_max -= 1
+
+        # 로마자 소문자 리스트
+        list_regex = r'((?:\n *i\. ?[^\n]*)+)\n'
+        list_count_max = len(re.findall(list_regex, self.render_data)) * 3
+        while 1:
+            list_data = re.search(list_regex, self.render_data)
+            if list_count_max < 0:
+                break
+            elif not list_data:
+                break
+            else:
+                list_data = list_data.group(1)
+                list_sub_regex = r'\n( *)i.(?:#([0-9]*))? ?([^\n]*)'
+
+                list_class = do_render_list_int_to('roman_small')
+                list_data = re.sub(list_sub_regex, list_class, list_data)
+
+                self.render_data = re.sub(list_regex, lambda x : ('\n<front_br><ul class="opennamu_ul">' + list_data + '</ul><back_br>\n'), self.render_data, 1)
+
+            list_count_max -= 1
+
     def do_render_remark(self):
         self.render_data = re.sub(r'\n##[^\n]+', '\n<front_br>', self.render_data)
 
@@ -1973,7 +2163,7 @@ class class_do_render_namumark:
                 toc_data_on == 1:
                 self.render_data = re.sub(r'<toc_no_auto>', '', self.render_data)
             else:
-                self.render_data = re.sub(r'(?P<in><h[1-6] id="[^"]*">)', '<br>' + self.data_toc + '\g<in>', self.render_data, 1)
+                self.render_data = re.sub(r'(?P<in><h[1-6] id="[^"]*">)', '<br>' + self.data_toc + '\\g<in>', self.render_data, 1)
         else:
             self.render_data = re.sub(r'<toc_need_part>', '', self.render_data)
             self.render_data = re.sub(r'<toc_no_auto>', '', self.render_data)
@@ -1981,7 +2171,7 @@ class class_do_render_namumark:
         def do_render_last_footnote(match):
             match = match.group(1)
 
-            find_regex = re.compile('<footnote_title id="' + match + '_title">((?:(?!<footnote_title|<\/footnote_title>).)*)<\/footnote_title>')
+            find_regex = re.compile(r'<footnote_title id="' + match + r'_title">((?:(?!<footnote_title|<\/footnote_title>).)*)<\/footnote_title>')
             find_data = re.search(find_regex, self.render_data)
             if find_data:
                 find_data = find_data.group(1)
@@ -2008,10 +2198,10 @@ class class_do_render_namumark:
             self.do_render_list()
             self.do_render_macro()
             self.do_render_link()
-            self.do_redner_footnote()
             self.do_render_text()
             self.do_render_hr()
             self.do_render_heading()
+            self.do_redner_footnote()
             
         self.do_render_last()
 
@@ -2020,6 +2210,7 @@ class class_do_render_namumark:
             self.render_data_js, # js
             {
                 'backlink' : self.data_backlink, # backlink
-                'include' : list(reversed(self.data_include)) # include data
+                'include' : list(reversed(self.data_include)), # include data
+                'footnote' : self.data_footnote_all # footnote
             } # other
         ]

+ 1 - 0
route/tool/func_tool.py

@@ -2,6 +2,7 @@ import urllib.parse
 import datetime
 import hashlib
 import flask
+import string
 import re
 
 import os

+ 21 - 57
route/topic.py

@@ -1,5 +1,8 @@
 from .tool.func import *
-from .api_topic import api_topic
+
+from .api_topic import api_topic, api_topic_thread_pre_render
+
+from .edit import edit_editor
 
 def topic(topic_num = 0, do_type = '', doc_name = 'Test'):
     with get_db_connect() as conn:
@@ -11,7 +14,12 @@ def topic(topic_num = 0, do_type = '', doc_name = 'Test'):
         if topic_view_acl == 1:
             return re_error('/ban')
 
+        ip = ip_check()
+
         if flask.request.method == 'POST' and do_type == '':
+            if do_edit_slow_check('thread') == 1:
+                return re_error('/error/42')
+
             name = flask.request.form.get('topic', 'Test')
             sub = flask.request.form.get('title', 'Test')
             
@@ -34,7 +42,6 @@ def topic(topic_num = 0, do_type = '', doc_name = 'Test'):
             else:
                 captcha_post('', 0)
 
-            ip = ip_check()
             today = get_time()
 
             if topic_acl == 1:
@@ -65,54 +72,15 @@ def topic(topic_num = 0, do_type = '', doc_name = 'Test'):
                         y_check = 1
 
                 if y_check == 1:
-                    add_alarm(match, ip + ' | <a href="/thread/' + topic_num + '#' + num + '">' + html.escape(name) + ' | ' + html.escape(sub) + ' | #' + num + '</a>')
-
-            cate_re = re.compile(r'\[\[((?:분류|category):(?:(?:(?!\]\]).)*))\]\]', re.I)
-            data = cate_re.sub('[br]', flask.request.form.get('content', 'Test').replace('\r', ''))
-
-            call_thread_regex = r"( |\n|^)(?:#([0-9]+))( |\n|$)"
-            call_thread_count = len(re.findall(call_thread_regex, data)) * 3
-            while 1:
-                rd_data = re.search(call_thread_regex, data)
-                if call_thread_count < 0:
-                    break
-                elif not rd_data:
-                    break
-                else:
-                    rd_data = rd_data.groups()
-
-                    curs.execute(db_change("select ip from topic where code = ? and id = ?"), [topic_num, rd_data[1]])
-                    ip_data = curs.fetchall()
-                    if ip_data and ip_or_user(ip_data[0][0]) == 0 and ip != ip_data[0][0]:
-                        add_alarm(ip_data[0][0], ip + ' | <a href="/thread/' + topic_num + '#' + num + '">' + html.escape(name) + ' | ' + html.escape(sub) + ' | #' + num + '</a>')
-
-                    data = re.sub(call_thread_regex, rd_data[0] + '<topic_a>#' + rd_data[1] + '</topic_a>' + rd_data[2], data, 1)
-
-                call_thread_count -= 1
-
-            call_user_regex = r"( |\n|^)(?:@([^ ]+))( |\n|$)"
-            call_user_count = len(re.findall(call_user_regex, data)) * 3
-            while 1:
-                rd_data = re.search(call_user_regex, data)
-                if call_user_count < 0:
-                    break
-                elif not rd_data:
-                    break
-                else:
-                    rd_data = rd_data.groups()
-
-                    curs.execute(db_change("select ip from history where ip = ? limit 1"), [rd_data[1]])
-                    ip_data = curs.fetchall()
-                    if not ip_data:
-                        curs.execute(db_change("select ip from topic where ip = ? limit 1"), [rd_data[1]])
-                        ip_data = curs.fetchall()
-
-                    if ip_data and ip_or_user(ip_data[0][0]) == 0 and ip != ip_data[0][0]:
-                        add_alarm(ip_data[0][0], ip + ' | <a href="/thread/' + topic_num + '#' + num + '">' + html.escape(name) + ' | ' + html.escape(sub) + ' | #' + num + '</a>')
-
-                    data = re.sub(call_user_regex, rd_data[0] + '<topic_call>@' + rd_data[1] + '</topic_call>' + rd_data[2], data, 1)
+                    add_alarm(match, ip, '<a href="/thread/' + topic_num + '#' + num + '">' + html.escape(name) + ' - ' + html.escape(sub) + '#' + num + '</a>')
+            
+            curs.execute(db_change("select ip from topic where code = ? and id = '1'"), [topic_num])
+            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 + '">' + html.escape(name) + ' - ' + html.escape(sub) + '#' + num + '</a>')
 
-                call_user_count -= 1
+            data = flask.request.form.get('content', 'Test').replace('\r', '')
+            data = api_topic_thread_pre_render(curs, data, num, ip, topic_num, name, sub)
 
             do_add_thread(
                 topic_num,
@@ -161,9 +129,7 @@ def topic(topic_num = 0, do_type = '', doc_name = 'Test'):
                 thread_data = thread_data.replace('\r', '')
 
                 thread_data_preview = render_set(
-                    doc_name = '', 
-                    doc_data = thread_data,
-                    data_in = ''
+                    doc_data = thread_data
                 )
 
             acl_display = 'display: none;' if topic_acl == 1 else ''
@@ -212,15 +178,13 @@ def topic(topic_num = 0, do_type = '', doc_name = 'Test'):
                             <hr class="main_hr">
                         </div>
                         
-                        <div>''' + edit_button('opennamu_edit_textarea') + '''</div>
-
-                        <textarea id="opennamu_edit_textarea" class="opennamu_textarea_100" placeholder="''' + topic_text + '''" name="content">''' + html.escape(thread_data) + '''</textarea>
+                        ''' + edit_editor(curs, ip, thread_data, 'thread') + '''
                         <hr class="main_hr">
                         
                         ''' + captcha_get() + ip_warning() + '''
                         
-                        <button id="opennamu_save_button" formaction="/thread/''' + topic_num + '''" type="submit">''' + load_lang('send') + '''</button>
-                        <button id="opennamu_preview_button" formaction="/thread_preview/''' + topic_num + '''#opennamu_edit_textarea" type="submit">''' + load_lang('preview') + '''</button>
+                        <button id="opennamu_save_button" formaction="/thread/''' + topic_num + '''" type="submit" onclick="do_monaco_to_textarea(); do_stop_exit_release();">''' + load_lang('send') + '''</button>
+                        <button id="opennamu_preview_button" formaction="/thread_preview/''' + topic_num + '''#opennamu_edit_textarea" type="submit" onclick="do_monaco_to_textarea(); do_stop_exit_release();">''' + load_lang('preview') + '''</button>
                     </form>
                     <hr class="main_hr">
                     

+ 1 - 0
route/topic_list.py

@@ -1,4 +1,5 @@
 from .tool.func import *
+
 from .api_topic import api_topic
 
 def topic_list(name = 'Test'):

+ 1 - 1
route/user_info.py

@@ -49,7 +49,7 @@ def user_info(name = ''):
                 <h2>''' + load_lang('admin') + '''</h2>
                 <ul class="opennamu_ul">
                     <li><a href="/auth/give/ban/''' + url_pas(ip) + '''">''' + ban_name + '''</a></li>
-                    <li><a href="/check/''' + url_pas(ip) + '''">''' + load_lang('check') + '''</a></li>
+                    <li><a href="/list/user/check/''' + url_pas(ip) + '''">''' + load_lang('check') + '''</a></li>
                 </ul>
             '''
         else:

+ 1 - 3
route/user_setting.py

@@ -7,8 +7,6 @@ def user_setting():
         support_language = ['default'] + get_init_set_list()['language']['list']
         
         ip = ip_check()
-        if ban_check(ip) == 1:
-            return re_error('/ban')
 
         if ip_or_user(ip) == 0:
             if flask.request.method == 'POST':
@@ -188,4 +186,4 @@ def user_setting():
                         </form>
                     ''',
                     menu = [['user', load_lang('return')]]
-                ))
+                ))

+ 9 - 0
route/user_setting_skin_set_main.py

@@ -64,6 +64,10 @@ def user_setting_skin_set_main_set_list():
             ['default', load_lang('default')],
             ['off', load_lang('off')],
             ['on', load_lang('use')]
+        ], 'main_css_table_scroll' : [
+            ['default', load_lang('default')],
+            ['off', load_lang('off')],
+            ['on', load_lang('use')]
         ]
     }
 
@@ -198,6 +202,11 @@ def user_setting_skin_set_main():
                         <select name="main_css_darkmode">
                             ''' + set_data["main_css_darkmode"] + '''
                         </select>
+                        <h3>''' + load_lang("table_scroll") + '''</h3>
+                        ''' + set_data_main["main_css_table_scroll"] + '''
+                        <select name="main_css_table_scroll">
+                            ''' + set_data["main_css_table_scroll"] + '''
+                        </select>
                         <h2>''' + load_lang("edit") + '''</h2>
                         <h3>''' + load_lang("image_paste") + '''</h3>
                         <sup>''' + load_lang('only_korean') + '''</sup> <sup>''' + load_lang('unavailable_in_monaco') + '''</sup>

+ 4 - 5
route/view_acl.py

@@ -83,7 +83,7 @@ def view_acl(name):
                         data_type = 'backlink'
                     )
 
-            markup_data = markup_data if markup_data != '' else 'default'
+            markup_data = markup_data if markup_data != '' else 'normal'
 
             if user_page == 1:
                 admin_check(5, check_data + ' (' + all_d + ')' + ' (' + markup_data + ')')
@@ -155,18 +155,17 @@ def view_acl(name):
                 <h2>''' + load_lang('markup') + '''</h2>
             '''
 
-
             curs.execute(db_change("select set_data from data_set where doc_name = ? and set_name = 'document_markup'"), [name])
             db_data = curs.fetchall()
             markup_load = db_data[0][0] if db_data and db_data[0][0] != '' else ''
 
-            markup_list = ['default'] + get_init_set_list('markup')['list']
+            markup_list = ['normal'] + get_init_set_list('markup')['list']
             markup_html = ''
             for for_a in markup_list:
                 if markup_load == for_a:
-                    markup_html = '<option value="' + (for_a if for_a != 'default' else '') + '">' + for_a + '</option>' + markup_html
+                    markup_html = '<option value="' + (for_a if for_a != 'normal' else '') + '">' + for_a + '</option>' + markup_html
                 else:
-                    markup_html += '<option value="' + (for_a if for_a != 'default' else '') + '">' + for_a + '</option>'
+                    markup_html += '<option value="' + (for_a if for_a != 'normal' else '') + '">' + for_a + '</option>'
             
             markup_html = '<select name="document_markup" ' + check_ok + '>' + markup_html + '</select>'
 

+ 4 - 4
route/view_diff.py

@@ -60,7 +60,7 @@ def view_diff(name = 'Test', num_a = 1, num_b = 1):
 
                         temp_list += [[line, for_a[0], for_a[1]]]
 
-                result = '<table style="width: 100%;"><tr><td colspan="2">r' + first + ' ➤ r' + second + '</td></tr>'
+                result = '<table style="width: 100%; white-space: pre-wrap;"><tr><td colspan="2">r' + first + ' ➤ r' + second + '</td></tr>'
                 result += '<tr><td style="width: 40px; user-select: none;">'
 
                 # 개행만 추가된 경우 조정 필요
@@ -76,11 +76,11 @@ def view_diff(name = 'Test', num_a = 1, num_b = 1):
                             result += '</td></tr><tr><td style="width: 40px; user-select: none;">' + str(line) + '</td><td>'
 
                     if for_a[1] == 1:
-                        result += '<span style="background: #eaf2c2;">' + for_a[2] + '</span>'
+                        result += '<span style="background: #eaf2c2;">' + html.escape(for_a[2]) + '</span>'
                     elif for_a[1] == 0:
-                        result += for_a[2]
+                        result += html.escape(for_a[2])
                     else:
-                        result += '<span style="background: #fadad7;">' + for_a[2] + '</span>'
+                        result += '<span style="background: #fadad7;">' + html.escape(for_a[2]) + '</span>'
 
                 result += '</td></tr></table>'
 

+ 1 - 1
route/view_read.py

@@ -144,7 +144,7 @@ def view_read(name = 'Test', doc_rev = '', doc_from = '', do_type = ''):
         end_data = render_set(
             doc_name = name,
             doc_data = data[0][0] if data else None,
-            data_in = 'from' if do_type == 'from' else ''
+            data_type = 'from' if do_type == 'from' else 'view'
         )
 
         if end_data == 'HTTP Request 401.3':

+ 1 - 1
route/vote_add.py

@@ -64,7 +64,7 @@ def vote_add():
                         '<hr class="main_hr">' + \
                         '<input type="checkbox" value="Y" name="open_select"> ' + load_lang('open_vote') + \
                         '<h2>' + load_lang('period') + '</h2>'
-                        '<input type="date" name="date" pattern="\d{4}-\d{2}-\d{2}">' + \
+                        '<input type="date" name="date" pattern="\\d{4}-\\d{2}-\\d{2}">' + \
                         '<hr class="main_hr">' + \
                         '<input type="checkbox" value="Y" name="limitless"> ' + load_lang('limitless') + \
                         '<h2>' + load_lang('acl') + '</h2>' + \

+ 2 - 2
route/vote_select.py

@@ -1,10 +1,10 @@
 from .tool.func import *
 
 def vote_select(num = 1):
-    num = str(num)
-    
     with get_db_connect() as conn:
         curs = conn.cursor()
+        
+        num = str(num)
 
         curs.execute(db_change('select name, subject, data, type from vote where id = ? and user = ""'), [num])
         data_list = curs.fetchall()

BIN
route_go/bin/main_easter_egg.amd64.bin


BIN
route_go/bin/main_easter_egg.amd64.exe


BIN
route_go/bin/main_easter_egg.arm64.bin


BIN
route_go/bin/main_easter_egg.arm64.exe


+ 22 - 0
route_go/linux_amd64.sh

@@ -0,0 +1,22 @@
+echo "file_name : "
+read file_name
+
+export GOOS=linux
+export GOARCH=amd64
+go build $file_name.go
+mv $file_name ./bin/$file_name.amd64.bin
+
+export GOOS=linux
+export GOARCH=arm64
+go build $file_name.go
+mv $file_name ./bin/$file_name.arm64.bin
+
+export GOOS=windows
+export GOARCH=amd64
+go build $file_name.go
+mv $file_name.exe ./bin/$file_name.amd64.exe
+
+export GOOS=windows
+export GOARCH=arm64
+go build $file_name.go
+mv $file_name.exe ./bin/$file_name.arm64.exe

+ 37 - 0
route_go/main_easter_egg.go

@@ -0,0 +1,37 @@
+package main
+
+import (
+	"fmt"
+	"math/rand"
+	"time"
+)
+
+func main() {
+	rand.Seed(time.Now().UnixNano())
+
+	select_list := []string{
+		"PWD0ZbR7AOY", // Shanghai Teahouse ~ Chinese Tea
+		"HoU29ljOmTE", // Flawless Clothing of Celestials
+		"PR2vUm-Ald8", // U.N. Owen Was Her
+		"opZoEmsu_Lo", // Night of Nights
+		"txZFFTusSvw", // Reach for the Moon ~ Immortal Smoke
+		"Ixq9xL2tvRU", // Phantom Ensemble
+		"-3IAx_r4Au0", // Entrusting This World to Idols ~ Idolatrize World
+		"wObZkycA6sc", // Last Remote
+		"hZxYLa97gDg", // Emotional Skyscraper ~ Cosmic Mind
+		"hwn2kw4eFJM", // Border of Life
+		"wX2t_8HOtiY", // Voyage 1969
+		"tLQjcf45fKE", // Necrofantasia
+		"7DvMRAMuMrU", // Where Is That Bustling Marketplace Now ~ Immemorial Marketeers
+		// Remix by NyxTheShield
+		"SXFP9HgWBYQ", // 세계는 귀엽게 만들어져 있다
+		"YDrgO0Oj3Fg", // 죽취비상
+		"wxWV_sUGPB0", // 디자이어 드라이브
+		"uw0h2O7UaZ8", // 100번째 블랙 마켓
+		"blE4lnfEWbU", // 일렉트릭 헤리티지
+		// Remix by KR. Palto47
+	}
+	select_str := select_list[rand.Intn(len(select_list)-1)]
+
+	fmt.Println("<iframe width=\"640\" height=\"360\" src=\"https://www.youtube.com/embed/" + select_str + "\" frameborder=\"0\" allowfullscreen></iframe>")
+}

+ 1 - 1
version.json

@@ -1,6 +1,6 @@
 {
     "beta" : {
-        "r_ver" : "v3.4.6-RC3-dev181",
+        "r_ver" : "v3.4.6-RC3-dev213",
         "c_ver" : "3500361",
         "s_ver" : "3500111"
     }

+ 4 - 0
views/main_css/css/main.css

@@ -377,4 +377,8 @@ s:hover, strike:hover, del:hover {
     content: "⬥";
     width: 1.2em;
     margin-left: -1.2em;
+}
+
+.opennamu_list_none {
+    list-style: none;
 }

+ 5 - 5
views/main_css/css/sub/dark.css

@@ -45,7 +45,7 @@ input::placeholder, textarea::placeholder, select::placeholder {
 }
 
 pre#syntax, pre#syntax code {
-    background: black;
+    background: #313236;
 }
 
 .hljs, .hljs-subst {
@@ -53,15 +53,15 @@ pre#syntax, pre#syntax code {
 }
 
 blockquote {
-    background-color: black;
-    border-left: 5px solid #eee;
+    background-color: #313236;
+    border-left: 4px solid #eee;
 }
 
 .opennamu_spead_footnote {
-    background-color: black;
+    background-color: #313236;
     color: white;
 }
 
 .opennamu_popup_footnote {
-    background-color: black;
+    background-color: #313236;
 }

+ 3 - 0
views/main_css/js/func/check_new_thread.ts

@@ -0,0 +1,3 @@
+function opennamu_check_new_thread(do_type : string = '') {
+    
+}

+ 5 - 1
views/main_css/js/func/func.js

@@ -27,7 +27,11 @@ function opennamu_get_main_skin_set(set_name) {
         ) {
             return document.cookie.match(opennamu_cookie_split_regex(set_name))[1];
         } else {
-            return text[set_name][0][0];
+            if(text[set_name]) {
+                return text[set_name][0][0];
+            } else {
+                return '';
+            }
         }
     });
 }

+ 15 - 0
views/main_css/js/route/bbs_w_post.js

@@ -0,0 +1,15 @@
+function opennamu_change_comment(get_id) {
+    var _a;
+    var input = document.querySelector('#opennamu_comment_select');
+    if (input != null) {
+        input.value = get_id;
+        (_a = document.getElementById('opennamu_edit_textarea')) === null || _a === void 0 ? void 0 : _a.focus();
+    }
+}
+function opennamu_return_comment() {
+    var _a;
+    var input = document.querySelector('#opennamu_comment_select');
+    if (input != null) {
+        (_a = document.getElementById(input.value)) === null || _a === void 0 ? void 0 : _a.focus();
+    }
+}

+ 14 - 0
views/main_css/js/route/bbs_w_post.ts

@@ -0,0 +1,14 @@
+function opennamu_change_comment(get_id : string): void {
+    const input = document.querySelector('#opennamu_comment_select') as HTMLInputElement | null;
+    if(input != null) {
+        input.value = get_id;
+        document.getElementById('opennamu_edit_textarea')?.focus();
+    }
+}
+
+function opennamu_return_comment(): void {
+    const input = document.querySelector('#opennamu_comment_select') as HTMLInputElement | null;
+    if(input != null) {
+        document.getElementById(input.value)?.focus();
+    }
+}

+ 52 - 1
views/main_css/js/route/editor.js

@@ -151,8 +151,57 @@ function do_monaco_to_textarea() {
     }
 }
 
+// https://github.com/microsoft/monaco-editor/issues/568
+class PlaceholderContentWidget {
+    static ID = 'editor.widget.placeholderHint';
+
+    constructor(placeholder, editor) {
+		this.placeholder = placeholder;
+		this.editor = editor;
+        // register a listener for editor code changes
+        editor.onDidChangeModelContent(() => this.onDidChangeModelContent());
+        // ensure that on initial load the placeholder is shown
+        this.onDidChangeModelContent();
+    }
+
+    onDidChangeModelContent() {
+        if (this.editor.getValue() === '') {
+            this.editor.addContentWidget(this);
+        } else {
+            this.editor.removeContentWidget(this);
+        }
+    }
+
+    getId() {
+        return PlaceholderContentWidget.ID;
+    }
+
+    getDomNode() {
+        if (!this.domNode) {
+            this.domNode = document.createElement('div');
+            this.domNode.style.width = 'max-content';
+            this.domNode.textContent = this.placeholder;
+            this.domNode.style.fontStyle = 'italic';
+            this.editor.applyFontInfo(this.domNode);
+        }
+
+        return this.domNode;
+    }
+
+    getPosition() {
+        return {
+            position: { lineNumber: 1, column: 1 },
+            preference: [monaco.editor.ContentWidgetPositionPreference.EXACT],
+        };
+    }
+
+    dispose() {
+        this.editor.removeContentWidget(this);
+    }
+}
+
 function do_monaco_init(monaco_thema) {
-    require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.37.1/min/vs' }});
+    require.config({ paths: { 'vs': 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.40.0/min/vs' }});
     require.config({ 'vs/nls': { availableLanguages: { '*': 'ko' } }});
     require(["vs/editor/editor.main"], function () {
         window.editor = monaco.editor.create(document.getElementById('opennamu_monaco_editor'), {
@@ -162,5 +211,7 @@ function do_monaco_init(monaco_thema) {
             wordWrap: true,
             theme: monaco_thema
         });
+
+        new PlaceholderContentWidget(document.getElementById('opennamu_edit_textarea').placeholder, window.editor);
     });
 }

+ 8 - 0
views/ringo/css/main.css

@@ -337,6 +337,10 @@ input, textarea, button, select {
     background-color: white;
 }
 
+input, textarea {
+    width: calc(100% - 20px);
+}
+
 #main_data button:hover {
     background-color: #eee;
 
@@ -393,4 +397,8 @@ input, textarea, button, select {
 
 #nav_bar a {
     color: black;
+}
+
+pre#syntax, pre#syntax code {
+    border: 0;
 }

+ 1 - 1
views/ringo/index.html

@@ -13,7 +13,7 @@
         <script src="/views/ringo/js/main.js?ver=2"></script>
         <script src="/views/ringo/js/sidebar.js?ver=1"></script>
         <script src="/views/ringo/js/skin_set.js?ver=1"></script>
-        <link rel="stylesheet" href="/views/ringo/css/main.css?ver=2">
+        <link rel="stylesheet" href="/views/ringo/css/main.css?ver=3">
         {% if request.cookies.get('main_css_darkmode', '') == '1' %}
             <link rel="stylesheet" href="/views/main_css/css/sub/dark.css?ver=1">
             <link rel="stylesheet" href="/views/ringo/css/dark.css?ver=1">

+ 4 - 0
views/tenshi/css/main.css

@@ -331,6 +331,10 @@ textarea, input, pre {
     box-shadow: 0 2px 4px 0 rgba(0,0,0,0.16), 0 2px 10px 0 rgba(0,0,0,0.12);
 }
 
+textarea, input {
+    width: calc(100% - 10px);
+}
+
 pre {
     padding: 10px;
 }

+ 1 - 1
views/tenshi/index.html

@@ -8,7 +8,7 @@
             <title>{{imp[0]}} - {{imp[1][0]}}</title>
         {% endif %}
         {{imp[3][3]|safe}}
-        <link rel="stylesheet" href="/views/tenshi/css/main.css?ver=21">
+        <link rel="stylesheet" href="/views/tenshi/css/main.css?ver=22">
         {% if request.cookies.get('main_css_darkmode', '') == '1' %}
             <link rel="stylesheet" href="/views/main_css/css/sub/dark.css?ver=1">
             <link rel="stylesheet" href="/views/tenshi/css/dark.css?ver=9">

+ 1 - 1
views/tenshi/info.json

@@ -1,5 +1,5 @@
 {
     "name" : "Tenshi",
-    "skin_ver" : "v2.0.5",
+    "skin_ver" : "v2.0.6",
     "require_ver" : "3500110"
 }

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません