Przeglądaj źródła

Merge pull request #749 from 2du/master

스테이블
잉여개발기 (SPDV) 6 lat temu
rodzic
commit
92687eca51
51 zmienionych plików z 882 dodań i 657 usunięć
  1. 3 3
      .gitignore
  2. 49 38
      app.py
  3. 26 26
      emergency_tool.py
  4. 14 17
      language/en-US.json
  5. 12 14
      language/ko-KR.json
  6. 81 0
      readme-en.md
  7. 0 61
      readme-ko.md
  8. 35 65
      readme.md
  9. 19 2
      route/api_topic_sub.py
  10. 32 14
      route/api_w.py
  11. 1 1
      route/edit.py
  12. 5 1
      route/func_title_random.py
  13. 1 1
      route/func_upload.py
  14. 33 21
      route/give_acl.py
  15. 28 19
      route/list_title_index.py
  16. 2 1
      route/login.py
  17. 2 1
      route/login_pw_change.py
  18. 2 1
      route/login_register.py
  19. 3 3
      route/main_views.py
  20. 27 37
      route/recent_changes.py
  21. 10 0
      route/recent_history_tool.py
  22. 4 4
      route/search_deep.py
  23. 7 2
      route/server_now_update.py
  24. 20 15
      route/setting.py
  25. 129 165
      route/tool/func.py
  26. 1 1
      route/tool/init.py
  27. 8 3
      route/tool/mark.py
  28. 91 46
      route/tool/set_mark/namu.py
  29. 2 3
      route/topic.py
  30. 0 2
      route/topic_tool.py
  31. 2 2
      route/user_custom_head_view.py
  32. 1 1
      route/user_info.py
  33. 5 10
      route/user_setting.py
  34. 10 3
      route/view_read.py
  35. 3 4
      route/watch_list.py
  36. 2 3
      route/watch_list_name.py
  37. 2 2
      version.json
  38. 6 3
      views/main_css/css/main.css
  39. 1 1
      views/main_css/js/do_insert_data.js
  40. 2 2
      views/main_css/js/do_open_folding.js
  41. 21 0
      views/main_css/js/do_open_foot.js
  42. 22 0
      views/main_css/js/dummy_do_open_foot.js
  43. 33 0
      views/main_css/js/load_include.js
  44. 8 10
      views/main_css/js/load_preview.js
  45. 0 12
      views/main_css/js/open_foot.js
  46. 33 0
      views/main_css/js/render_html.js
  47. 1 1
      views/main_css/js/topic_main_load.js
  48. 1 1
      views/main_css/js/topic_top_load.js
  49. 50 17
      views/neo_yousoro/css/main.css
  50. 31 17
      views/neo_yousoro/index.html
  51. 1 1
      views/neo_yousoro/info.json

+ 3 - 3
.gitignore

@@ -1,5 +1,6 @@
 __pycache__
 /app_session
+data/set.json
 .vscode
 
 *.db
@@ -9,9 +10,8 @@ images
 robots.txt
 
 views/liberty
-views/yousoro
-views/super_lite
 views/buma
 views/before_namu
 views/acme
-views/sl-open
+views/sl_open
+views/nitori

+ 49 - 38
app.py

@@ -22,35 +22,25 @@ print('----')
 app_var = json.loads(open('data/app_var.json', encoding='utf-8').read())
 
 # DB
-all_src = []
-for i_data in os.listdir("."):
-    f_src = re.search("(.+)\.db$", i_data)
-    if f_src:
-        all_src += [f_src.groups()[0]]
-
-if len(all_src) == 0:
-    print('DB name (data) : ', end = '')
-    
-    db_name = input()
-    if db_name == '':
-        db_name = 'data'
-elif len(all_src) > 1:
-    db_num = 1
-
-    for i_data in all_src:
-        print(str(db_num) + ' : ' + i_data)
-
-        db_num += 1
-
-    print('----')
-    print('Number : ', end = '')
-    db_name = all_src[int(number_check(input())) - 1]
-    print('----')
-else:
-    db_name = all_src[0]
-
-if len(all_src) == 1:
-    print('DB\'s name : ' + db_name)
+try:
+    set_data = json.loads(open('data/set.json').read())
+except:
+    if os.getenv('NAMU_DB') != None:
+        set_data = { "db" : os.getenv('NAMU_DB') }
+    else:
+        print('DB name (data) : ', end = '')
+        
+        new_json = str(input())
+        if new_json == '':
+            new_json = 'data'
+            
+        with open('data/set.json', 'w') as f:
+            f.write('{ "db" : "' + new_json + '" }')
+            
+        set_data = json.loads(open('data/set.json').read())
+        
+print('DB name : ' + set_data['db'])
+db_name = set_data['db']
             
 if os.path.exists(db_name + '.db'):
     setup_tool = 0
@@ -269,8 +259,7 @@ print('----')
 if back_time != 0:
     print('Back up state : ' + str(back_time) + ' hours')
     
-    if __name__ == '__main__':
-        back_up()
+    back_up()
 else:
     print('Back up state : Turn off')
 
@@ -289,9 +278,31 @@ else:
 
         print('----')
         print('Skin update required')
-        
+
 conn.commit()
 
+def count_all_title():
+    curs.execute("select count(title) from data")
+    count_data = curs.fetchall()
+    if count_data:
+        count_data = count_data[0][0]
+    else:
+        count_data = 0
+
+    curs.execute('delete from other where name = "count_all_title"')
+    curs.execute('insert into other (name, data) values ("count_all_title", ?)', [str(count_data)])
+
+    conn.commit()
+
+    threading.Timer(60 * 60 * 24, count_all_title).start()
+
+curs.execute('select data from other where name = "count_all_title"')
+all_title = curs.fetchall()
+if not all_title:
+    curs.execute('insert into other (name, data) values ("count_all_title", "0")')
+
+count_all_title()  
+
 # Func
 @app.route('/del_alarm')
 def alarm_del():
@@ -361,7 +372,7 @@ def server_restart():
 
 @app.route('/update', methods=['GET', 'POST'])
 def server_now_update():
-    return server_now_update_2(conn)
+    return server_now_update_2(conn, r_ver)
 
 @app.route('/oauth_setting', methods=['GET', 'POST'])
 def setting_oauth():
@@ -631,12 +642,12 @@ def main_file(data = None):
 @app.errorhandler(404)
 def main_error_404(e):
     return main_error_404_2(conn)
+    
+app.secret_key = rep_key
+app.wsgi_app = werkzeug.debug.DebuggedApplication(app.wsgi_app, True)
+app.debug = True
 
-if __name__=="__main__":
-    app.secret_key = rep_key
-    app.wsgi_app = werkzeug.debug.DebuggedApplication(app.wsgi_app, True)
-    app.debug = True
-
+if __name__ == "__main__":
     http_server = tornado.httpserver.HTTPServer(tornado.wsgi.WSGIContainer(app))
     http_server.listen(server_set['port'], address = server_set['host'])
     

+ 26 - 26
emergency_tool.py

@@ -1,31 +1,25 @@
 from route.tool.func import *
 from route.tool.mark import load_conn2, namumark
 
-all_src = []
-for i_data in os.listdir("."):
-    f_src = re.search("(.+)\.db$", i_data)
-    if f_src:
-        all_src += [f_src.groups()[0]]
-
-if len(all_src) == 0:
-    exit()
-elif len(all_src) > 1:
-    db_num = 1
-
-    for i_data in all_src:
-        print(str(db_num) + ' : ' + i_data)
+try:
+    set_data = json.loads(open('data/set.json').read())
+except:
+    if os.getenv('NAMU_DB') != None:
+        set_data = { "db" : os.getenv('NAMU_DB') }
+    else:
+        print('DB name (data) : ', end = '')
         
-        db_num += 1
-
-    print('----')
-    print('Number : ', end = '')    
-    db_name = all_src[int(number_check(input())) - 1]
-else:
-    db_name = all_src[0]
-
-if len(all_src) == 1:
-    print('----')
-    print('DB\'s name : ' + db_name)
+        new_json = str(input())
+        if new_json == '':
+            new_json = 'data'
+            
+        with open('data/set.json', 'w') as f:
+            f.write('{ "db" : "' + new_json + '" }')
+            
+        set_data = json.loads(open('data/set.json').read())
+        
+print('DB name : ' + set_data['db'])
+db_name = set_data['db']
 
 conn = sqlite3.connect(db_name + '.db', check_same_thread = False)
 curs = conn.cursor()
@@ -42,6 +36,7 @@ print('6. Change skin')
 print('7. Change password')
 print('8. Reset version')
 print('9. New DB create')
+print('10. Delete set.json')
 
 print('----')
 print('Select : ', end = '')
@@ -135,15 +130,20 @@ elif what_i_do == '7':
     curs.execute("update user set pw = ? where id = ?", [hashed, user_name])
 elif what_i_do == '8':
     curs.execute("update other set data = '00000' where name = 'ver'")
-else:
+elif what_i_do == '9':
     print('----')
-    print('DB\'s name (data) : ', end = '')
+    print('DB name (data) : ', end = '')
     
     db_name = input()
     if db_name == '':
         db_name = 'data'
 
     sqlite3.connect(db_name + '.db', check_same_thread = False)
+elif what_i_do == '10':
+    try:
+        os.remove('data/set.json')
+    except:
+        pass
 
 conn.commit()
 

+ 14 - 17
language/en-US.json

@@ -88,6 +88,9 @@
         "pass" : "Passing",
         "file_name" : "File name",
         "pinned" : "Pinned",
+        "markup" : "Markup",
+        "title" : "Title",
+        "reference" : "Reference",
         "_comment_1.1_" : "Time",
             "second" : "Second(s)",
             "hour" : "Hour(s)",
@@ -151,7 +154,7 @@
         "count" : "Number of Contributions",
         "alarm" : "Notice(s)",
         "user_document" : "User[s] document",
-        "user_head" : "User[s] <head>",
+        "user_head" : "User[s] <HEAD>",
         "user_document_acl" : "User[s] document ACL",
         "encryption_method" : "Encryption method",
         "check_key" : "Check authentication key",
@@ -169,9 +172,9 @@
         "closed_discussion" : "Closed discussion",
         "agreed_discussion" : "Agreed discussion",
         "history_delete" : "History delete",
-        "markup" : "Markup",
-        "title" : "Title",
         "direct_input" : "Direct input",
+        "acl_record" : "ACL record",
+        "last_edit_time" : "Last edited time",
         "_comment_2.1_" : "Filter",
             "_comment_2.1.1_" : "List",
                 "interwiki_list" : "Interwiki(s) list",
@@ -199,8 +202,9 @@
             "_comment_2.2.1_" : "List",
                 "main_setting" : "Main settings",
                 "text_setting" : "Text settings",
-                "main_head" : "Global <head>",
-                "main_body" : "Global <body>",
+                "main_head" : "Global <HEAD>",
+                "main_body" : "Top of body",
+                "main_bottom_body" : "Bottom of body",
             "_comment_2.2.2_" : "Main",
                 "wiki_name" : "Wiki[s] name",
                 "wiki_logo" : "Wiki[s] logo",
@@ -260,35 +264,28 @@
             "_comment_2.3.3_" : "Record",
                 "edit_record" : "Edit record",
                 "discussion_record" : "Discussion record",
-        "_comment_2.4_" : "Editing textarea",
-            "edit_button_link" : "Link",
-            "edit_button_footnote" : "Footnote",
-            "edit_button_macro" : "Macro",
-            "edit_button_color" : "Color",
-            "edit_button_bold" : "Bold",
-            "edit_button_strike" : "Strike",
-            "edit_button_big" : "Big",
-        "_comment_2.5_" : "Topic tool",
+        "_comment_2.4_" : "Topic tool",
             "topic_tool" : "Discussion tool",
             "topic_state" : "Discussion status",
-        "_comment_2.6_" : "Period",
+        "_comment_2.5_" : "Period",
             "1_day" : "1 day",
             "5_day" : "5 days",
             "30_day" : "30 days",
             "180_day" : "180 days",
             "360_day" : "360 days",
-        "_comment_2.7_" : "ACL",
+        "_comment_2.6_" : "ACL",
             "admin_acl" : "Admin only",
             "member_acl" : "Member only",
             "50_edit_acl" : "Only members with 50 or more all document edits",
             "all_acl" : "All users",
             "email_acl" : "Only users with email",
+            "owner_acl" : "Owner only",
     "_comment_3_" : "Long",
         "ie_no_data_required" : "Operation cannot continue because all required data has not been collected.",
         "oauth_settings_not_found" : "Administrator has not provided any data about using this feature.",
         "oauth_disabled" : "Administrator has disabled this feature.",
         "http_warring" : "Warning : If you are not on HTTPS connection, Your information can be leaked. We won't response to that.",
-        "user_head_warring" : "User[s] <head> will deleted if you close the browser or sign-in",
+        "user_head_warring" : "User[s] <HEAD> will deleted if you close the browser or sign-in",
         "no_login_warring" : "Non-login status. IP is logged when working with non-login.",
         "user_reset_sign" : "Your account information has changed like this.",
         "update_warring" : "Manual updates are recommended if your version is 0.2 or lower than the latest version. For Windows, the contents of the route folder disappear.",

+ 12 - 14
language/ko-KR.json

@@ -35,7 +35,6 @@
     "acl": "ACL",
     "history_hide_authority": "역사 숨김 권한",
     "go": "이동",
-    "edit_button_bold": "강조",
     "document_acl": "문서 ACL",
     "secret_key": "비밀키",
     "raw": "원본",
@@ -69,7 +68,6 @@
     "password_error": "비밀번호가 다릅니다.",
     "default": "기본값",
     "wiki_name": "위키 이름",
-    "edit_button_big": "크게",
     "acl_document_list": "ACL 문서 목록",
     "connection": "연결",
     "oauth_disabled": "관리자가 이 기능을 비활성화시켰습니다.",
@@ -78,7 +76,6 @@
     "band_blocked": "대역 차단됨",
     "password_diffrent_error": "입력한 비밀번호와 비밀번호 확인이 서로 다릅니다.",
     "180_day": "180일",
-    "edit_button_footnote": "각주",
     "markup_enabled": "문법 사용 가능",
     "all_acl": "모든 사용자",
     "send": "전송",
@@ -113,7 +110,6 @@
     "user_document": "사용자 문서",
     "id": "아이디",
     "no_login_warring": "비로그인 상태입니다. 편집시 지금 접속한 IP 명의로 기록됩니다.",
-    "edit_button_link": "링크",
     "decument_404_error": "이 문서는 존재하지 않습니다.",
     "key": "키",
     "lastest": "최신",
@@ -124,7 +120,7 @@
     "new_password": "새 비밀번호",
     "authority_use_list": "권한 사용 목록",
     "alarm": "알림",
-    "main_body": "전역 <body>",
+    "main_body": "본문 상단",
     "all_document_list": "모든 문서 목록",
     "update": "업데이트",
     "login_able": "로그인 가능",
@@ -158,7 +154,6 @@
     "interwiki_list": "인터위키 목록",
     "email_change": "이메일 변경",
     "edit_button_paragraph": "문단",
-    "edit_button_macro": "매크로",
     "check": "검사",
     "admin": "관리자",
     "edit_filter_list": "편집 필터 목록",
@@ -186,17 +181,15 @@
     "text_setting": "문구 설정",
     "document_acl_authority": "문서 ACL 관리 권한",
     "wiki_logo": "위키 로고",
-    "edit_button_color": "색상",
     "google_imap_required": "Google IMAP 설정 필요",
     "adsense_setting": "애드센스 설정",
     "google_email": "Google 이메일",
     "previous": "이전",
-    "edit_button_strike": "취소선",
     "name_or_ip_or_regex": "ID or IP or 정규식",
     "editor": "에디터",
     "wiki_secret_key": "위키 비밀키",
     "band_ban": "대역 차단",
-    "http_warring": "경고: HTTPS 연결을 사용하지 않는다면 개인정보가 유출될 수 있습니다. 이 사항에 의해 입는 피해는 사용자에게 책임이 있음을 알려드립니다.",
+    "http_warring": "경고 : HTTPS 연결을 사용하지 않는다면 개인정보가 유출될 수 있습니다. 이 사항에 의해 입는 피해는 사용자에게 책임이 있음을 알려드립니다.",
     "update_warring": "최신 버전보다 0.2 버전 이상 낮은 경우 수동 업데이트를 권장 드립니다. 윈도우의 경우 route 폴더의 내용이 사라집니다.",
     "discussion_name": "토론명",
     "discussion": "토론",
@@ -219,7 +212,7 @@
     "name": "이름",
     "period": "기간",
     "writer": "작성자",
-    "oauth_signin_facebook": "Facebook 아이디로 로그인",
+    "oauth_signin_facebook": "페이스북 아이디로 로그인",
     "long_id_error": "ID는 20자보다 짧아야 합니다.",
     "edit_record_error": "수정 요약은 500자를 넘길 수 없습니다.",
     "revert": "복원",
@@ -247,7 +240,7 @@
     "id_char_error": "오직 한글과 알파벳, 공백만 사용 가능합니다.",
     "id_filter_add": "ID 필터 추가",
     "skin": "스킨",
-    "user_head": "사용자 <head>",
+    "user_head": "사용자 <HEAD>",
     "agreement": "동의",
     "stop": "중지",
     "ie_no_data_required": "이 기능을 수행하는데 필요한 데이터가 제공되지 않았습니다.",
@@ -266,14 +259,14 @@
     "recent_change": "최근 편집",
     "destruction": "취소",
     "count": "기여 횟수",
-    "main_head": "전역 <head>",
+    "main_head": "전역 <HEAD>",
     "recent_ban": "최근 차단",
     "google_app_password": "Google 앱 비밀번호",
     "id_filter_list": "ID 필터 목록",
     "other": "기타",
     "edit": "편집",
     "open_discussion_list": "열린 토론 목록",
-    "user_head_warring": "비로그인시 브라우저를 닫거나 로그인시 사용자의 <head>는 삭제됩니다.",
+    "user_head_warring": "비로그인시 브라우저를 닫거나 로그인시 사용자의 <HEAD>는 삭제됩니다.",
     "email_required": "이메일 필요",
     "1_day": "1일",
     "regex_error": "정규표현식에 오류가 있습니다.",
@@ -295,5 +288,10 @@
     "edit_tool_add": "편집 도구 추가",
     "image_license_list": "이미지 라이선스 목록",
     "image_license_add": "이미지 라이선스 추가",
-    "direct_input": "직접 입력"
+    "direct_input": "직접 입력",
+    "acl_record": "ACL 기록",
+    "main_bottom_body": "본문 하단",
+    "reference" : "참고",
+    "owner_acl" : "소유자만",
+    "last_edit_time" : "최근 수정 시각"
 }

+ 81 - 0
readme-en.md

@@ -0,0 +1,81 @@
+openNAMU
+====
+[![Python 3.5 or later Required](https://img.shields.io/badge/python-3.5%20or%20higher-blue.svg)](https://python.org)
+[![LICENSE](https://img.shields.io/badge/license-BSD%203--Clause-lightgrey.svg)](./LICENSE)
+
+![](https://raw.githubusercontent.com/2du/openNAMU/master/.github/logo.png)
+
+openNAMU is a Python-based wiki engine. You can use openNAMU by installing Python and its dependency modules, and you can modify the code yourself to create more specialized wikis.
+
+### Index
+ * [Getting Started](#getting-started)
+ * [Clone](#clone)
+ * [Contribute](#contribute)
+ * [License](#license)
+ * [Authors](#authors)
+ * [Etc.](#etc)
+
+# Getting Started
+openNAMU is based upon Python, and it requires a Python environment.
+
+### Download Releases
+Download the [release version of openNAMU](https://github.com/2du/openNAMU/releases), and unzip the file. It is also possible to download releases by [cloning this repository](#Clone).
+
+### Install Modules
+Windows
+```
+pip install -r requirements.txt
+```
+
+Linux
+```
+pip3 install -r requirements.txt
+```
+## Launching Application
+Windows
+```
+python app.py
+```
+
+Linux
+```
+python3 app.py
+```
+
+## Publishing Application
+
+# Clone
+You can clone this repository by entering the following command at the terminal (command prompt):
+## Stable
+ * `git clone -b stable https://github.com/2du/openNAMU.git`
+
+## Beta
+ * `git clone -b master https://github.com/2du/openNAMU.git`
+
+# Contribute
+openNAMU may have some untested bugs. Your use of openNAMU and bug discovery will help develop openNAMU.
+[Create Issues](https://github.com/2du/openNAMU/issues/new)
+
+openNAMU is open source project. Add new features and request pull requests. 
+[Create Pull Requests](https://github.com/2du/openNAMU/compare)
+
+# Lisence
+openNAMU is protected by [BSD 3-Clause License](./LICNESE). Please refer to the documentation for details.
+
+## External Projects
+ * Quotes icon [Dave Gandy](http://www.flaticon.com/free-icon/quote-left_25672)
+ * Syntax highlighting [highlightjs](https://highlightjs.org/)
+ * Numerical expression [MathJax](https://www.mathjax.org/)
+ * Handling Keyboard Shortcuts [shortcut.js](http://www.openjs.com/scripts/events/keyboard_shortcuts/)
+
+# Authors
+ * [Reference](https://github.com/2DU/openNAMU/graphs/contributors)
+
+## Special Thanks
+ * [Team Croatia](https://github.com/TeamCroatia)
+ * Basix
+ * Efrit
+ * Other chat rooms
+
+# Etc.
+ * Owner rights are granted to the first registor.

+ 0 - 61
readme-ko.md

@@ -1,61 +0,0 @@
-openNAMU
-====
-[![Python 3.5 이상의 버전 필요](https://img.shields.io/badge/python-3.5%20or%20higher-blue.svg)](https://python.org)
-[![라이선스](https://img.shields.io/badge/license-BSD%203--Clause-lightgrey.svg)](./LICENSE)
-
-![](https://raw.githubusercontent.com/2du/openNAMU/master/.github/logo.png)
-
-openNAMU는 파이썬 기반의 위키 엔진입니다. 파이썬과 그 의존성 모듈만 설치하면 사용할 수 있으며, 코드를 직접 수정하여 좀 더 주제에 특화된 위키를 만들 수 있습니다.
-
-### 목차
- * [클론](#클론)
- * [기여](#기여)
- * [라이선스](#라이선스)
- * [기여자 목록](#기여자-목록)
- * [기타](#기타)
-
-## 시작하기
-openNAMU는 파이썬 환경에서 동작하는 파이썬 애플리케이션으로, 파이썬 환경을 필요로 합니다.
-
-쉬운 openNAMU 설치를 위해 openNAMU 가이드를 따로 생성해두었으며, [이곳](https://github.com/Make-openNAMU/guide)에서 확인하실 수 있습니다.
-
-### 가이드 목록
- * [파이썬 설치](https://github.com/Make-openNAMU/guide/blob/master/articles/ko-kr/install-python.md)
- * [openNAMU 시작](https://github.com/Make-openNAMU/guide/blob/master/articles/ko-kr/start-opennamu.md)
-   * [openNAMU 도커 시작(실험적 기능)](https://github.com/Make-openNAMU/guide/blob/master/articles/ko-kr/docker-install.md)
-
-## 클론
-아래 명령을 터미널(명령 프롬프트)에 입력하여 본 리포지토리를 클론할 수 있습니다.
-### 일반
- * `git clone -b stable https://github.com/2du/openNAMU.git`
-
-### 개발중
- * `git clone -b master https://github.com/2du/openNAMU.git`
-
-## 기여
-openNAMU에는 검증되지 않은 몇가지 버그가 존재할 수 있습니다. 당신의 openNAMU 사용과 버그 발견은 openNAMU의 발전을 돕습니다.
-[이슈 생성하기](https://github.com/2du/openNAMU/issues/new)
-
-openNAMU는 완전한 오픈소스 프로젝트입니다. 새로운 기능을 추가하고 Pull Request를 생성해보세요.
-[Pull Request 생성하기](https://github.com/2du/openNAMU/compare)
-
-## 라이선스
-openNAMU 프로젝트는 [BSD 3-Clause License](./LICENSE)(이하 이용허락)의 보호를 받고 있으며, openNAMU 프로젝트를 사용하고자 한다면 를 준수해야 합니다. 본 이용허락를 위반할 경우 개발자는 DMCA Takedown 등 관련 제재를 관계자에게 요청할 권리가 있으며, 그 책임은 모두 이용허락 위반 사용자에게 있습니다. 자세한 내용은 문서를 참고하세요.
-
-### 포함된 외부 프로젝트
- * Quotes icon - [Dave Gandy](http://www.flaticon.com/free-icon/quote-left_25672)
- * Syntax highlighting - [highlightjs](https://highlightjs.org/) 
- * Numerical expression - [MathJax](https://www.mathjax.org/)
- * Handling Keyboard Shortcuts [shortcut.js](http://www.openjs.com/scripts/events/keyboard_shortcuts/)
-
-## 기여자 목록
- * [참고](https://github.com/2DU/openNAMU/graphs/contributors)
-
-### 도움을 주신 분들
- * [Team Croatia](https://github.com/TeamCroatia)
- * Basix
- * Efrit
- * Other chat rooms
-
-## 기타
- * 첫 가입자에게 소유자 권한이 부여됩니다.

+ 35 - 65
readme.md

@@ -1,87 +1,57 @@
-openNAMU
+오픈나무
 ====
-[![Python 3.5 or later Required](https://img.shields.io/badge/python-3.5%20or%20higher-blue.svg)](https://python.org)
-[![LICENSE](https://img.shields.io/badge/license-BSD%203--Clause-lightgrey.svg)](./LICENSE)
+[![Python 3.5 이상의 버전 필요](https://img.shields.io/badge/python-3.5%20or%20higher-blue.svg)](https://python.org)
+[![라이선스](https://img.shields.io/badge/license-BSD%203--Clause-lightgrey.svg)](./LICENSE)
 
 ![](https://raw.githubusercontent.com/2du/openNAMU/master/.github/logo.png)
 
-openNAMU is a Python-based wiki engine. You can use openNAMU by installing Python and its dependency modules, and you can modify the code yourself to create more specialized wikis.
+오픈나무는 파이썬 기반의 위키 엔진입니다. 파이썬과 그 의존성 모듈만 설치하면 사용할 수 있으며, 코드를 직접 수정하여 좀 더 주제에 특화된 위키를 만들 수 있습니다.
 
- * [(README for Korean)](./readme-ko.md)
+ * [(README for english)](./readme-en.md)
+### 목차
+ * [클론](#클론)
+ * [기여](#기여)
+ * [라이선스](#라이선스)
+ * [기여자 목록](#기여자-목록)
+ * [기타](#기타)
 
-### Index
- * [Getting Started](#getting-started)
- * [Clone](#clone)
- * [Contribute](#contribute)
- * [License](#license)
- * [Authors](#authors)
- * [Etc.](#etc)
+## 시작하기
+오픈나무는 파이썬 환경에서 동작하는 파이썬 애플리케이션으로, 파이썬 환경을 필요로 합니다.
 
-# Getting Started
-openNAMU is based upon Python, and it requires a Python environment.
+쉬운 오픈나무 설치를 위해 오픈나무 가이드를 따로 생성해두었으며, [이곳](https://github.com/2du/openNAMU/wiki/%EC%84%A4%EC%B9%98%EB%B2%95)에서 확인하실 수 있습니다.
 
-## Set-Ups
-### Install Python
-See [Python Installation Guide(KR)](https://github.com/404-sdok/how-to-python/blob/master/0.md).
-
-### Download Releases
-Download the [release version of openNAMU](https://github.com/2du/openNAMU/releases), and unzip the file. It is also possible to download releases by [cloning this repository](#Clone).
-
-### Install Modules
-Windows
-```
-pip install -r requirements.txt
-```
-
-Linux
-```
-pip3 install -r requirements.txt
-```
-## Launching Application
-Windows
-```
-python app.py
-```
-
-Linux
-```
-python3 app.py
-```
-
-## Publishing Application
-
-# Clone
-You can clone this repository by entering the following command at the terminal (command prompt):
-## Stable
+## 클론
+아래 명령을 터미널(명령 프롬프트)에 입력하여 본 리포지토리를 클론할 수 있습니다.
+### 일반
  * `git clone -b stable https://github.com/2du/openNAMU.git`
 
-## Beta
+### 개발중
  * `git clone -b master https://github.com/2du/openNAMU.git`
 
-# Contribute
-openNAMU may have some untested bugs. Your use of openNAMU and bug discovery will help develop openNAMU.
-[Create Issues](https://github.com/2du/openNAMU/issues/new)
+## 기여
+오픈나무에는 검증되지 않은 몇가지 버그가 존재할 수 있습니다. 당신의 오픈나무 사용과 버그 발견은 오픈나무의 발전을 돕습니다.
+[이슈 생성하기](https://github.com/2du/openNAMU/issues/new)
 
-openNAMU is open source project. Add new features and request pull requests. 
-[Create Pull Requests](https://github.com/2du/openNAMU/compare)
+오픈나무는 완전한 오픈소스 프로젝트입니다. 새로운 기능을 추가하고 Pull Request를 생성해보세요.
+[Pull Request 생성하기](https://github.com/2du/openNAMU/compare)
 
-# Lisence
-openNAMU is protected by [BSD 3-Clause License](./LICNESE). Please refer to the documentation for details.
+## 라이선스
+오픈나무 프로젝트는 [BSD 3-Clause License](./LICENSE)(이하 이용허락)의 보호를 받고 있으며, 오픈나무 프로젝트를 사용하고자 한다면 라이선스를 준수해야 합니다. 본 이용허락를 위반할 경우 개발자는 DMCA Takedown 등 관련 제재를 관계자에게 요청할 권리가 있으며, 그 책임은 모두 이용허락 위반 사용자에게 있습니다. 자세한 내용은 문서를 참고하세요.
 
-## External Projects
- * Quotes icon [Dave Gandy](http://www.flaticon.com/free-icon/quote-left_25672)
- * Syntax highlighting [highlightjs](https://highlightjs.org/)
- * Numerical expression [MathJax](https://www.mathjax.org/)
+### 포함된 외부 프로젝트
+ * Quotes icon - [Dave Gandy](http://www.flaticon.com/free-icon/quote-left_25672)
+ * Syntax highlighting - [highlightjs](https://highlightjs.org/) 
+ * Numerical expression - [MathJax](https://www.mathjax.org/)
  * Handling Keyboard Shortcuts [shortcut.js](http://www.openjs.com/scripts/events/keyboard_shortcuts/)
 
-# Authors
- * [Reference](https://github.com/2DU/openNAMU/graphs/contributors)
+## 기여자 목록
+ * [참고](https://github.com/2DU/openNAMU/graphs/contributors)
 
-## Special Thanks
+### 도움을 주신 분들
  * [Team Croatia](https://github.com/TeamCroatia)
  * Basix
  * Efrit
- * Other chat rooms
+ * 기타 채팅방 사람들
 
-# Etc.
- * Owner rights are granted to the first registor.
+## 기타
+ * 첫 가입자에게 소유자 권한이 부여됩니다.

+ 19 - 2
route/api_topic_sub.py

@@ -55,21 +55,28 @@ def api_topic_sub_2(conn, name, sub, time):
                     t_color = 'toron_color'
                     
                 ip = ip_pas(i[3])
+                plus_ip = ''
                 
                 curs.execute('select acl from user where id = ?', [i[3]])
                 u_acl = curs.fetchall()
                 if u_acl and u_acl[0][0] != 'user':
-                    ip += ' <a href="javascript:void(0);" title="' + load_lang('admin') + '">★</a>'
+                    plus_ip = '<b>' + i[3] + '</b>'
 
                 if admin == 1 or b_color != 'toron_color_grey':
                     ip += ' <a href="/topic/' + url_pas(name) + '/sub/' + url_pas(sub) + '/admin/' + i[0] + '">(' + load_lang('discussion_tool') + ')</a>'
 
                 curs.execute("select end from ban where block = ?", [i[3]])
                 if curs.fetchall():
-                    ip += ' <a href="javascript:void(0);" title="' + load_lang('blocked') + '">†</a>'
+                    if plus_ip != '':
+                        plus_ip = '<s>' + plus_ip + '</s>'
+                    else:
+                        plus_ip = '<s>' + i[3] + '</s>'
                     
                 if t_data_f == '':
                     t_data_f = '[br]'
+                    
+                if plus_ip != '':
+                    ip = ip.replace('>' + i[3] + '<', '>' + plus_ip + '<')
             
                 all_data = '''
                     <table id="toron">
@@ -91,6 +98,16 @@ def api_topic_sub_2(conn, name, sub, time):
                     "data" : all_data
                 }
             else:
+                if i[4] != 'O' or (i[4] == 'O' and admin == 1):
+                    t_data_f = i[1]
+                else:
+                    curs.execute("select who from re_admin where what = ? order by time desc limit 1", ['blind (' + name + ' - ' + sub + '#' + str(i[0]) + ')'])
+                    who_blind = curs.fetchall()
+                    if who_blind:
+                        t_data_f = '[[user:' + who_blind[0][0] + ']] block'
+                    else:
+                        t_data_f = 'block'
+
                 json_data[i[0]] = {
                     "data" : t_data_f,
                     "date" : i[2],

+ 32 - 14
route/api_w.py

@@ -3,19 +3,37 @@ from .tool.func import *
 def api_w_2(conn, name):
     curs = conn.cursor()
 
-    if acl_check(name, 'render') != 1:
-        if flask.request.method == 'POST':
-            json_data = { "title" : name, "data" : render_set(title = name, data = flask.request.form.get('data', '')) }
-            
-            return flask.jsonify(json_data)
+    if flask.request.args.get('exist', None):
+        curs.execute("select title from data where title = ?", [name])
+        if curs.fetchall():
+            return flask.jsonify({ "exist" : "1" })
         else:
-            curs.execute("select data from data where title = ?", [name])
-            data = curs.fetchall()
-            if data:
-                json_data = { "title" : name, "data" : render_set(title = name, data = data[0][0]) }
-            
-                return flask.jsonify(json_data)
-            else:
-                return flask.jsonify({})
+            return flask.jsonify({})
     else:
-        return flask.jsonify({})
+        if acl_check(name, 'render') != 1:
+            if flask.request.method == 'POST':
+                g_data = render_set(title = name, data = flask.request.form.get('data', ''), num = 2)
+                
+                return flask.jsonify({ "title" : name, "data" : g_data[0], "js_data" : g_data[1] })
+            else:
+                curs.execute("select data from data where title = ?", [name])
+                data = curs.fetchall()
+                if data:
+                    if flask.request.args.get('include', 'include_1'):
+                        include_re = re.compile('\[include\(((?:(?!\)\]).)+)\)\]', re.I)
+                        category_re = re.compile('\[\[(?:(?:category|분류):(?:(?!\[\[|\]\]).)+)\]\]', re.I)
+                        
+                        json_data = include_re.sub('', data[0][0])
+                        json_data = category_re.sub('', json_data, )
+                        
+                        g_data = render_set(title = name, data = json_data, num = 2, include = flask.request.args.get('include', 'include_1'))
+                    else:
+                        json_data = g_data[0]
+
+                        g_data = render_set(title = name, data = json_data, num = 2)
+
+                    return flask.jsonify({ "title" : name, "data" : g_data[0], "js_data" : g_data[1] })
+                else:
+                    return flask.jsonify({})
+        else:
+            return flask.jsonify({})

+ 1 - 1
route/edit.py

@@ -123,7 +123,7 @@ def edit_2(conn, name):
                     <hr class=\"main_hr\">
                     ''' + captcha_get() + ip_warring() + '''
                     <button id="save" type="submit">''' + load_lang('save') + '''</button>
-                    <button id="preview" type="button" onclick="load_preview(\'''' + name + '\')">' + load_lang('preview') + '''</button>
+                    <button id="preview" type="button" onclick="load_preview(\'''' + url_pas(name) + '\')">' + load_lang('preview') + '''</button>
                 </form>
                 ''' + b_text + '''
                 <hr class=\"main_hr\">

+ 5 - 1
route/func_title_random.py

@@ -3,7 +3,11 @@ from .tool.func import *
 def func_title_random_2(conn):
     curs = conn.cursor()
 
-    curs.execute("select title from data order by random() limit 1")
+    curs.execute("" + \
+        "select title from data " + \
+        "where title not like 'user:%' and title not like 'category:%' and title not like 'file:%'" + \
+        "order by random() limit 1" + \
+    "")
     data = curs.fetchall()
     if data:
         return redirect('/w/' + url_pas(data[0][0]))

+ 1 - 1
route/func_upload.py

@@ -51,7 +51,7 @@ def func_upload_2(conn):
             if flask.request.form.get('f_lice', None):
                 lice = flask.request.form.get('f_lice', None)
             else:
-                if custom()[2] == 0:
+                if ip_or_user(ip) != 0:
                     lice = ip
                 else:
                     lice = '[[user:' + ip + ']]'

+ 33 - 21
route/give_acl.py

@@ -4,6 +4,7 @@ def give_acl_2(conn, name):
     curs = conn.cursor()
 
     check_ok = ''
+    ip = ip_check()
     
     if flask.request.method == 'POST':
         check_data = 'acl (' + name + ')'
@@ -12,44 +13,54 @@ def give_acl_2(conn, name):
     
     user_data = re.search('^user:(.+)$', name)
     if user_data:
-        if check_data and custom()[2] == 0:
+        if check_data and ip_or_user(ip) != 0:
             return redirect('/login')
         
         if user_data.groups()[0] != ip_check():
-            if admin_check(5, check_data) != 1:
+            if admin_check(5) != 1:
                 if check_data:
                     return re_error('/error/3')
                 else:
                     check_ok = 'disabled'
     else:
-        if admin_check(5, check_data) != 1:
+        if admin_check(5) != 1:
             if check_data:
                 return re_error('/error/3')
             else:
                 check_ok = 'disabled'
 
     if flask.request.method == 'POST':
-        decu = flask.request.form.get('decu', '')
-        view = flask.request.form.get('view', '')
-
         curs.execute("select title from acl where title = ?", [name])
         if curs.fetchall():
-            curs.execute("update acl set decu = ? where title = ?", [decu, name])
+            curs.execute("update acl set decu = ? where title = ?", [flask.request.form.get('decu', ''), name])
             curs.execute("update acl set dis = ? where title = ?", [flask.request.form.get('dis', ''), name])
             curs.execute("update acl set why = ? where title = ?", [flask.request.form.get('why', ''), name])
-            curs.execute("update acl set view = ? where title = ?", [view, name])
+            curs.execute("update acl set view = ? where title = ?", [flask.request.form.get('view', ''), name])
         else:
             curs.execute("insert into acl (title, decu, dis, why, view) values (?, ?, ?, ?, ?)", [
                 name, 
-                decu, 
+                flask.request.form.get('decu', ''), 
                 flask.request.form.get('dis', ''), 
                 flask.request.form.get('why', ''), 
-                view
+                flask.request.form.get('view', '')
             ])
         
         curs.execute("select title from acl where title = ? and decu = '' and dis = '' and view = ''", [name])
         if curs.fetchall():
             curs.execute("delete from acl where title = ?", [name])
+            
+        all_d = ''
+        for i in ['decu', 'dis', 'view']:
+            if flask.request.form.get(i, '') == '':
+                all_d += 'normal'
+                if i != 'view':
+                    all_d += ' | '
+            else:
+                all_d += flask.request.form.get(i, '')
+                if i != 'view':
+                    all_d += ' | '
+            
+        admin_check(5, check_data + ' (' + all_d + ')')
 
         conn.commit()
             
@@ -58,19 +69,19 @@ def give_acl_2(conn, name):
         data = '<h2>' + load_lang('document_acl') + '</h2><hr class=\"main_hr\"><select name="decu" ' + check_ok + '>'
     
         if re.search('^user:', name):
-            acl_list = [['', 'normal'], ['user', 'member'], ['all', 'all']]
+            acl_list = ['', 'user', 'all']
         else:
-            acl_list = [['', 'normal'], ['user', 'member'], ['admin', 'admin'], ['50_edit', '50 edit'], ['email', 'email']]
+            acl_list = ['', 'user', 'admin', 'owner', '50_edit', 'email']
         
         curs.execute("select decu from acl where title = ?", [name])
         acl_data = curs.fetchall()
         for data_list in acl_list:
-            if acl_data and acl_data[0][0] == data_list[0]:
+            if acl_data and acl_data[0][0] == data_list:
                 check = 'selected="selected"'
             else:
                 check = ''
             
-            data += '<option value="' + data_list[0] + '" ' + check + '>' + data_list[1] + '</option>'
+            data += '<option value="' + data_list + '" ' + check + '>' + (data_list if data_list != '' else 'normal') + '</option>'
             
         data += '</select>'
         
@@ -80,23 +91,23 @@ def give_acl_2(conn, name):
             curs.execute("select dis, why, view from acl where title = ?", [name])
             acl_data = curs.fetchall()
             for data_list in acl_list:
-                if acl_data and acl_data[0][0] == data_list[0]:
+                if acl_data and acl_data[0][0] == data_list:
                     check = 'selected="selected"'
                 else:
                     check = ''
                     
-                data += '<option value="' + data_list[0] + '" ' + check + '>' + data_list[1] + '</option>'
+                data += '<option value="' + data_list + '" ' + check + '>' + (data_list if data_list != '' else 'normal') + '</option>'
                 
             data += '</select>'
 
             data += '<hr class=\"main_hr\"><h2>' + load_lang('view_acl') + '</h2><hr class=\"main_hr\"><select name="view" ' + check_ok + '>'
             for data_list in acl_list:
-                if acl_data and acl_data[0][2] == data_list[0]:
+                if acl_data and acl_data[0][2] == data_list:
                     check = 'selected="selected"'
                 else:
                     check = ''
                     
-                data += '<option value="' + data_list[0] + '" ' + check + '>' + data_list[1] + '</option>'
+                data += '<option value="' + data_list + '" ' + check + '>' + (data_list if data_list != '' else 'normal') + '</option>'
                 
             data += '''
                 </select>
@@ -104,10 +115,11 @@ def give_acl_2(conn, name):
                 <ul>
                     <li>normal : ''' + load_lang('default') + '''</li>
                     <li>admin : ''' + load_lang('admin_acl') + '''</li>
-                    <li>member : ''' + load_lang('member_acl') + '''</li>
-                    <li>50 edit : ''' + load_lang('50_edit_acl') + '''</li>
+                    <li>user : ''' + load_lang('member_acl') + '''</li>
+                    <li>50_edit : ''' + load_lang('50_edit_acl') + '''</li>
                     <li>all : ''' + load_lang('all_acl') + '''</li>
                     <li>email : ''' + load_lang('email_acl') + '''</li>
+                    <li>owner : ''' + load_lang('owner_acl') + '''</li>
                 </ul>
             '''
                 
@@ -126,5 +138,5 @@ def give_acl_2(conn, name):
                     <button type="submit" ''' + check_ok + '''>''' + load_lang('save') + '''</button>
                 </form>
             ''',
-            menu = [['w/' + url_pas(name), load_lang('document')], ['manager', load_lang('admin')]]
+            menu = [['w/' + url_pas(name), load_lang('document')], ['manager', load_lang('admin')], ['admin_log?search=' + url_pas('acl (' + name + ')'), load_lang('acl_record')]]
         ))

+ 28 - 19
route/list_title_index.py

@@ -29,38 +29,47 @@ def list_title_index_2(conn):
     if page == 1:
         count_end = []
 
-        curs.execute("select count(title) from data")
-        count = curs.fetchall()
-        if count:
-            count_end += [count[0][0]]
-        else:
-            count_end += [0]
-
-        sql_list = [load_lang('template', 1).lower() + ':', 'category:', 'user:', 'file:']
-        for sql in sql_list:
-            curs.execute("select count(title) from data where title like ?", [sql + '%'])
+        curs.execute('select data from other where name = "count_all_title"')
+        all_title = curs.fetchall()
+        if int(all_title[0][0]) < 50000:
+            curs.execute("select count(title) from data")
             count = curs.fetchall()
             if count:
                 count_end += [count[0][0]]
             else:
                 count_end += [0]
 
-        count_end += [count_end[0] - count_end[1]  - count_end[2]  - count_end[3]  - count_end[4]]
+            sql_list = ['category:', 'user:', 'file:']
+            for sql in sql_list:
+                curs.execute("select count(title) from data where title like ?", [sql + '%'])
+                count = curs.fetchall()
+                if count:
+                    count_end += [count[0][0]]
+                else:
+                    count_end += [0]
+
+            count_end += [count_end[0] - count_end[1]  - count_end[2]  - count_end[3]]
         
-        data += '''
+            data += '''
                 </ul>
                 <hr class=\"main_hr\">
                 <ul>
-                    <li>all : ''' + str(count_end[0]) + '''</li>
+                    <li>''' + load_lang('all') + ' : ' + str(count_end[0]) + '''</li>
+                </ul>
+                <hr class=\"main_hr\">
+                <ul>
+                    <li>''' + load_lang('category') + ' : ' + str(count_end[1]) + '''</li>
+                    <li>''' + load_lang('user_document') + ' : ' + str(count_end[2]) + '''</li>
+                    <li>''' + load_lang('file') + ' : ' + str(count_end[3]) + '''</li>
+                    <li>''' + load_lang('other') + ' : ' + str(count_end[4]) + '''</li>
+            '''
+        else:
+            data += '''
                 </ul>
                 <hr class=\"main_hr\">
                 <ul>
-                    <li>''' + load_lang('template') + ' : ' + str(count_end[1]) + '''</li>
-                    <li>''' + load_lang('category') + ' : ' + str(count_end[2]) + '''</li>
-                    <li>''' + load_lang('user') + ' : ' + str(count_end[3]) + '''</li>
-                    <li>''' + load_lang('file') + ' : ' + str(count_end[4]) + '''</li>
-                    <li>other : ''' + str(count_end[5]) + '''</li>
-                '''
+                    <li>''' + load_lang('all') + ' : ' + all_title[0][0] + '''</li>
+            '''
 
     data += '</ul>' + next_fix('/title_index?num=' + str(num) + '&page=', page, title_list, num)
     sub = ' (' + str(num) + ')'

+ 2 - 1
route/login.py

@@ -3,7 +3,8 @@ from .tool.func import *
 def login_2(conn):
     curs = conn.cursor()
 
-    if custom()[2] != 0:
+    ip = ip_check()
+    if ip_or_user(ip) == 0:
         return redirect('/user')
     
     if ban_check(tool = 'login') == 1:

+ 2 - 1
route/login_pw_change.py

@@ -6,7 +6,8 @@ def login_pw_change_2(conn):
     if ban_check() == 1:
         return re_error('/ban')
 
-    if custom()[2] == 0:
+    ip = ip_check()
+    if ip_or_user(ip) != 0:
         return redirect('/login')
 
     if flask.request.method == 'POST':

+ 2 - 1
route/login_register.py

@@ -6,7 +6,8 @@ def login_register_2(conn):
     if ban_check() == 1:
         return re_error('/ban')
 
-    if custom()[2] != 0:
+    ip = ip_check()
+    if ip_or_user(ip) == 0:
         return redirect('/user')
 
     if not admin_check() == 1:

+ 3 - 3
route/main_views.py

@@ -13,17 +13,17 @@ def main_views_2(conn, name):
         c_open = open('./views/' + name, encoding='utf-8')
         f_open = c_open.read()
         c_open.close()
-        return flask.Response(easy_minify(f_open, 'css'), mimetype="text/css")
+        return flask.Response(easy_minify(f_open, 'css'), mimetype = "text/css")
     elif g[0] == 'js':
         c_open = open('./views/' + name, encoding='utf-8')
         f_open = c_open.read()
         c_open.close()
-        return flask.Response(easy_minify(f_open, 'js'), mimetype="text/js")
+        return flask.Response(easy_minify(f_open, 'js'), mimetype = "text/js")
     elif g[0] == 'html':
         c_open = open('./views/' + name, encoding='utf-8')
         f_open = c_open.read()
         c_open.close()
-        return flask.Response(easy_minify(f_open), mimetype="text/html")
+        return flask.Response(easy_minify(f_open), mimetype = "text/html")
     else:
         if re.search('\/', name):
             m = re.search('^(.*)\/(.*)$', name)

+ 27 - 37
route/recent_changes.py

@@ -39,28 +39,20 @@ def recent_changes_2(conn, name, tool):
                 
                 # 기본적인 move만 구현
                 tool_select = flask.request.args.get('tool', None)
-                if tool_select:
-                    if tool_select == 'move':
-                        curs.execute('''
-                            select id, title, date, ip, send, leng from history
-                            where send like ? or send like ?
-                            order by id + 0 desc
-                            limit ?, '50'
-                        ''', ['%(<a>' + name +'</a>%', '%<a>' + name + '</a> move)', str(sql_num)])
-                    else:
-                        curs.execute('''
-                            select id, title, date, ip, send, leng from history
-                            where title = ?
-                            order by id + 0 desc
-                            limit ?, '50'
-                        ''', [name, str(sql_num)])
+                if tool_select and tool_select == 'move':
+                    curs.execute('' + \
+                        'select id, title, date, ip, send, leng from history ' + \
+                        'where send like ? or send like ? ' + \
+                        'order by id + 0 desc ' + \
+                        "limit ?, '50'" + \
+                    '', ['%(<a>' + name +'</a>%', '%<a>' + name + '</a> move)', str(sql_num)])
                 else:
-                    curs.execute('''
-                        select id, title, date, ip, send, leng from history
-                        where title = ?
-                        order by id + 0 desc
-                        limit ?, '50'
-                    ''', [name, str(sql_num)])
+                    curs.execute('' + \
+                        'select id, title, date, ip, send, leng from history ' + \
+                        'where title = ? ' + \
+                        'order by id + 0 desc ' + \
+                        "limit ?, '50'" + \
+                    '', [name, str(sql_num)])
             else:
                 div +=  '''
                         <td id="main_table_width">''' + load_lang('document_name') + '''</td>
@@ -85,22 +77,17 @@ def recent_changes_2(conn, name, tool):
                     <td id="main_table_width">''' + load_lang('time') + '''</td>
                 </tr>
             '''
-            if flask.request.args.get('set', 'normal') == 'user':
-                curs.execute('''
-                    select id, title, date, ip, send, leng from history 
-                    where title like 'user:%' 
-                    order by date desc 
-                    limit ?, 50
-                ''', [str(sql_num)])
-            else:
+
+            set_user = flask.request.args.get('set', 'normal')
+            if set_user == 'normal':
                 div = '<a href="?set=user">(' + load_lang('user_document') + ')</a>' + div
 
-                curs.execute('''
-                    select id, title, date, ip, send, leng from history 
-                    where not title like 'user:%' 
-                    order by date desc 
-                    limit ?, 50
-                ''', [str(sql_num)])
+            curs.execute('' + \
+                'select id, title, date, ip, send, leng from history ' + \
+                "where " + ('' if set_user == 'user' else 'not ') + "title like 'user:%' " + \
+                'order by date desc ' + \
+                'limit ?, 50' + \
+            '', [str(sql_num)])
 
         data_list = curs.fetchall()
         for data in data_list:    
@@ -119,7 +106,10 @@ def recent_changes_2(conn, name, tool):
                 leng = '<span style="color:gray;">(' + data[5] + ')</span>'
                 
             ip = ip_pas(data[3])
-            m_tool = '<a href="/history_tool/' + url_pas(data[1]) + '?num=' + data[0] + '">(' + load_lang('tool') + ')</a>'
+            if tool == 'history':
+                m_tool = '<a href="/history_tool/' + url_pas(data[1]) + '?num=' + data[0] + '&type=history">(' + load_lang('tool') + ')</a>'
+            else:
+                m_tool = '<a href="/history_tool/' + url_pas(data[1]) + '?num=' + data[0] + '">(' + load_lang('tool') + ')</a>'
             
             style = ['', '']
             date = data[2]
@@ -212,7 +202,7 @@ def recent_changes_2(conn, name, tool):
                 
             div += next_fix('/recent_changes?num=', num, data_list)
 
-            if flask.request.args.get('set', 'normal') == 'user':
+            if set_user == 'user':
                 sub = ' (' + load_lang('user') + ')'
                 menu = [['recent_changes', load_lang('return')]]
         

+ 10 - 0
route/recent_history_tool.py

@@ -18,6 +18,16 @@ def recent_history_tool_2(conn, name):
             <li>
                 <a href="/diff/''' + url_pas(name) + '?first=''' + str(int(num) - 1) + '&second=' + num + '">' + load_lang('compare') + '''</a>
             </li>
+        '''
+
+    if flask.request.args.get('type', '') == 'history':
+        data += '''
+            <li>
+                <a href="/revert/''' + url_pas(name) + '?num=' + num + '">' + load_lang('revert') + '''</a>
+            </li>
+        '''
+    elif (int(num) - 1) > 0:
+        data += '''
             <li>
                 <a href="/revert/''' + url_pas(name) + '?num=' + str(int(num) - 1) + '">' + load_lang('revert') + '''</a>
             </li>

+ 4 - 4
route/search_deep.py

@@ -33,10 +33,10 @@ def search_deep_2(conn, name):
             <ul>
             '''
 
-    curs.execute(
-        "select distinct title, case when title like ? then '제목' else '내용' \
-        end from data where title like ? or data like ? order by case \
-        when title like ? then 1 else 2 end limit ?, '50'",
+    curs.execute("" + \
+        "select distinct title, case when title like ? then '제목' else '내용' " + \
+        "end from data where title like ? or data like ? order by case " + \
+        "when title like ? then 1 else 2 end limit ?, '50'",
         ['%' + name + '%', '%' + name + '%', '%' + name + '%', '%' + name + '%', str(sql_num)]
     )
     all_list = curs.fetchall()

+ 7 - 2
route/server_now_update.py

@@ -1,6 +1,6 @@
 from .tool.func import *
 
-def server_now_update_2(conn):
+def server_now_update_2(conn, r_ver):
     curs = conn.cursor()
 
     if admin_check() != 1:
@@ -53,11 +53,16 @@ def server_now_update_2(conn):
             imp = [load_lang('update'), wiki_set(), custom(), other2([0, 0])],
             data = load_lang('update_warring') + '''
                 <hr class=\"main_hr\">
-                <a href="https://github.com/2du/openNAMU/blob/master/app.py">(Master)</a> <a href="https://github.com/2du/openNAMU/blob/stable/app.py">(Stable)</a>
+                <ul>
+                    <li>''' + load_lang('version') + ' : ' + r_ver + '''</li>
+                    <li id="ver_send" style="display: none;">''' + load_lang('lastest') + ''' : </li>
+                </ul>
+                <a href="https://github.com/2du/openNAMU">(Master)</a> <a href="https://github.com/2du/openNAMU/tree/stable">(Stable)</a>
                 <hr class=\"main_hr\">
                 <form method="post">
                     <button type="submit">''' + load_lang('update') + '''</button>
                 </form>
+                <script>load_ver();</script>
             ''',
             menu = [['manager', load_lang('return')]]
         ))

+ 20 - 15
route/setting.py

@@ -13,7 +13,8 @@ def setting_2(conn, num):
             load_lang('main_head'),
             load_lang('main_body'),
             'robots.txt',
-            'Google'
+            'Google',
+            load_lang('main_bottom_body'),
         ]
         
         x = 0
@@ -97,23 +98,19 @@ def setting_2(conn, num):
             conn.commit()
             
             div = ''
-            acl_list = [
-                [load_lang('member'), 'login'], 
-                [load_lang('ip'), 'normal'], 
-                [load_lang('admin'), 'admin']
-            ]
+            acl_list = ['normal', 'user', 'admin', 'owner', '50_edit', 'email']
             for i in acl_list:
-                if i[1] == d_list[6]:
-                    div = '<option value="' + i[1] + '">' + i[0] + '</option>' + div
+                if i == d_list[6]:
+                    div = '<option value="' + i + '">' + i + '</option>' + div
                 else:
-                    div += '<option value="' + i[1] + '">' + i[0] + '</option>'
+                    div += '<option value="' + i + '">' + i + '</option>'
 
             div4 = ''
             for i in acl_list:
-                if i[1] == d_list[14]:
-                    div4 = '<option value="' + i[1] + '">' + i[0] + '</option>' + div4
+                if i == d_list[14]:
+                    div4 = '<option value="' + i + '">' + i + '</option>' + div4
                 else:
-                    div4 += '<option value="' + i[1] + '">' + i[0] + '</option>'
+                    div4 += '<option value="' + i + '">' + i + '</option>'
 
             ch_1 = ''
             if d_list[7]:
@@ -177,7 +174,7 @@ def setting_2(conn, num):
                         <hr class=\"main_hr\">
                         <select name="skin">''' + div2 + '''</select>
                         <hr class=\"main_hr\">
-                        <span>''' + load_lang('default_acl') + '''</span>
+                        <span>''' + load_lang('default_acl') + '</span> <a href="/acl/TEST">(' + load_lang('reference') + ''')</a>
                         <hr class=\"main_hr\">
                         <select name="edit">''' + div + '''</select>
                         <hr class=\"main_hr\">
@@ -189,7 +186,7 @@ def setting_2(conn, num):
                         <hr class=\"main_hr\">
                         <input type="checkbox" name="ip_view" ''' + ch_2 + '''> ''' + load_lang('hide_ip') + '''
                         <hr class=\"main_hr\">
-                        <input type="checkbox" name="email_have" ''' + ch_3 + '''> ''' + load_lang('email_required') + ''' {<a href="/setting/6">''' + load_lang('google_imap_required') + '''</a>}
+                        <input type="checkbox" name="email_have" ''' + ch_3 + '''> ''' + load_lang('email_required') + ' <a href="/setting/6">{' + load_lang('google_imap_required') + '''}</a>
                         <hr class=\"main_hr\">
                         <span>''' + load_lang('wiki_host') + '''</span>
                         <hr class=\"main_hr\">
@@ -310,12 +307,16 @@ def setting_2(conn, num):
                 ''',
                 menu = [['setting', load_lang('return')]]
             ))
-    elif num == 3 or num == 4:
+    elif num == 3 or num == 4 or num == 7:
         if flask.request.method == 'POST':
             if num == 4:
                 info_d = 'body'
                 end_r = '4'
                 coverage = ''
+            elif num == 7:
+                info_d = 'bottom_body'
+                end_r = '7'
+                coverage = ''
             else:
                 info_d = 'head'
                 end_r = '3'
@@ -344,6 +345,10 @@ def setting_2(conn, num):
                 curs.execute("select data from other where name = 'body'")
                 title = '_body'
                 start = ''
+            elif num == 7:
+                curs.execute("select data from other where name = 'bottom_body'")
+                title = '_bottom_body'
+                start = ''
             else:
                 curs.execute("select data from other where name = 'head' and coverage = ?", [flask.request.args.get('skin', '')])
                 title = '_head'

+ 129 - 165
route/tool/func.py

@@ -134,21 +134,21 @@ def last_change(data):
 def easy_minify(data, tool = None):    
     return last_change(data)
 
-def render_set(title = '', data = '', num = 0, s_data = 0):
+def render_set(title = '', data = '', num = 0, s_data = 0, include = None):
     if acl_check(title, 'render') == 1:
         return 'HTTP Request 401.3'
     elif s_data == 1:
         return data
     else:
         if data != None:
-            return namumark(title, data, num)
+            return namumark(title, data, num, include)
         else:
             return 'HTTP Request 404'
 
 def captcha_get():
     data = ''
 
-    if custom()[2] == 0:
+    if ip_or_user() != 0:
         curs.execute('select data from other where name = "recaptcha"')
         recaptcha = curs.fetchall()
         if recaptcha and recaptcha[0][0] != '':
@@ -167,7 +167,7 @@ def update():
         for i in db_data:
             curs.execute("update acl set decu = ? where title = ?", [i[1], i[0]])
 
-        print('fix table acl column dec to decu')
+        print('Fix table acl column dec to decu')
         print('----')
     except:
         pass
@@ -235,7 +235,7 @@ def pw_check(data, data2, type_d = 'no', id_d = ''):
 
 def captcha_post(re_data, num = 1):
     if num == 1:
-        if custom()[2] == 0 and captcha_get() != '':
+        if ip_or_user() != 0 and captcha_get() != '':
             curs.execute('select data from other where name = "sec_re"')
             sec_re = curs.fetchall()
             if sec_re and sec_re[0][0] != '':
@@ -308,23 +308,17 @@ def update_oauth(provider, target, content):
 
     return 'Done'
 
-def ip_or_user(data):
+def ip_or_user(data = ''):
+    if data == '':
+        data = ip_check()
+
     if re.search('(\.|:)', data):
         return 1
     else:
         return 0
 
 def edit_button():
-    insert_list = [
-        ['[[name|view]]', load_lang('edit_button_link')], 
-        ['[* data]', load_lang('edit_button_footnote')], 
-        ['[macro(data)]', load_lang('edit_button_macro')],
-        ['{{{#color data}}}', load_lang('edit_button_color')], 
-        ["\\'\\'\\'data\\'\\'\\'", load_lang('edit_button_bold')],
-        ["~~data~~", load_lang('edit_button_strike')],
-        ["{{{+number data}}}", load_lang('edit_button_big')],
-        ["== name ==", load_lang('edit_button_paragraph')]
-    ]
+    insert_list = []
     
     curs.execute("select html, plus from html_filter where kind = 'edit_top'")
     db_data = curs.fetchall()
@@ -333,12 +327,15 @@ def edit_button():
 
     data = ''
     for insert_data in insert_list:
-        data += '<a href="javascript:insert_data(\'content\', \'' + insert_data[0] + '\')">(' + insert_data[1] + ')</a> '
+        data += '<a href="javascript:do_insert_data(\'content\', \'' + insert_data[0] + '\')">(' + insert_data[1] + ')</a> '
+
+    if admin_check() == 1:
+        data += (' ' if data != '' else '') + '<a href="/edit_top">(' + load_lang('add') + ')</a>'
 
     return data + '<hr class=\"main_hr\">'
 
 def ip_warring():
-    if custom()[2] == 0:    
+    if ip_or_user() != 0:
         curs.execute('select data from other where name = "no_login_warring"')
         data = curs.fetchall()
         if data and data[0][0] != '':
@@ -385,11 +382,23 @@ def next_fix(link, num, page, end = 50):
 
 def other2(data):
     req_list = ''
+    
+    css_filter = {}
     for i_data in os.listdir(os.path.join("views", "main_css", "css")):
-        req_list += '<link rel="stylesheet" href="/views/main_css/css/' + i_data + '">'
+        if i_data in css_filter:
+            req_list += '<link rel="stylesheet" href="/views/main_css/css/' + i_data + '?ver=' + css_filter[i_data] + '">'
+        else:
+            req_list += '<link rel="stylesheet" href="/views/main_css/css/' + i_data + '?ver=1">'
     
+    js_filter = {
+        'load_include.js' : '2',
+        'do_open_foot.js' : '4'
+    }
     for i_data in os.listdir(os.path.join("views", "main_css", "js")):
-        req_list += '<script src="/views/main_css/js/' + i_data + '"></script>'
+        if i_data in js_filter:
+            req_list += '<script src="/views/main_css/js/' + i_data + '?ver=' + js_filter[i_data] + '"></script>'
+        else:
+            req_list += '<script src="/views/main_css/js/' + i_data + '?ver=1"></script>'
 
     data += ['', '''
         <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css">
@@ -438,12 +447,18 @@ def wiki_set(num = 1):
         curs.execute("select data from other where name = 'head' and coverage = ?", [skin_check(1)])
         db_data = curs.fetchall()
         if db_data and db_data[0][0] != '':
-            data_list += [db_data[0][0]]
+            if len(re.findall('<', db_data[0][0])) % 2 != 1:
+                data_list += [db_data[0][0]]
+            else:
+                data_list += ['']
         else:
             curs.execute("select data from other where name = 'head' and coverage = ''")
             db_data = curs.fetchall()
             if db_data and db_data[0][0] != '':
-                data_list += [db_data[0][0]]
+                if len(re.findall('<', db_data[0][0])) % 2 != 1:
+                    data_list += [db_data[0][0]]
+                else:
+                    data_list += ['']
             else:
                 data_list += ['']
 
@@ -521,18 +536,21 @@ def admin_check(num = None, what = None):
     if user:
         reset = 0
 
+        back_num = num
         while 1:
-            if num == 1 and reset == 0:
+            if num == 1:
                 check = 'ban'
-            elif num == 3 and reset == 0:
+            elif num == 2:
+                check = 'nothing'
+            elif num == 3:
                 check = 'toron'
-            elif num == 4 and reset == 0:
+            elif num == 4:
                 check = 'check'
-            elif num == 5 and reset == 0:
+            elif num == 5:
                 check = 'acl'
-            elif num == 6 and reset == 0:
+            elif num == 6:
                 check = 'hidel'
-            elif num == 7 and reset == 0:
+            elif num == 7:
                 check = 'give'
             else:
                 check = 'owner'
@@ -545,8 +563,15 @@ def admin_check(num = None, what = None):
 
                 return 1
             else:
-                if reset == 0:
-                    reset = 1
+                if back_num == 'all':
+                    if num == 'all':
+                        num = 1
+                    elif num != 8:
+                        num += 1
+                    else:
+                        break
+                elif num:
+                    num = None
                 else:
                     break
                     
@@ -579,17 +604,16 @@ def ip_pas(raw_ip):
 
 def custom():
     if 'head' in flask.session:
-        user_head = flask.session['head']
+        if len(re.findall('<', flask.session['head'])) % 2 != 1:
+            user_head = flask.session['head']
+        else:
+            user_head = ''
     else:
         user_head = ''
 
     ip = ip_check()
     if ip_or_user(ip) == 0:
-        curs.execute('select name from alarm where name = ? limit 1', [ip])
-        if curs.fetchall():
-            user_icon = 2
-        else:
-            user_icon = 1
+        user_icon = 1
     else:
         user_icon = 0
 
@@ -607,8 +631,28 @@ def custom():
         user_name = ip
     else:
         user_name = load_lang('user')
+        
+    if admin_check('all') == 1:
+        user_admin = '1'
+    else:
+        user_admin = '0'
+        
+    if ban_check() == 1:
+        user_ban = '1'
+    else:
+        user_ban = '0'
+
+    if user_icon == 1:
+        curs.execute("select count(name) from alarm where name = ?", [ip])
+        count = curs.fetchall()
+        if count:
+            user_notice = str(count[0][0])
+        else:
+            user_notice = '0'
+    else:
+        user_notice = '0'
 
-    return ['', '', user_icon, user_head, email, user_name]
+    return ['', '', user_icon, user_head, email, user_name, user_admin, user_ban, user_notice]
 
 def load_skin(data = '', set_n = 0):
     div2 = ''
@@ -661,44 +705,13 @@ def load_skin(data = '', set_n = 0):
 
     return div2
 
-def acl_check(name, tool = ''):
+def acl_check(name = 'test', tool = '', sub = 'test'):
     ip = ip_check()
     
-    if tool == 'render':
-        curs.execute("select view from acl where title = ?", [name])
-        acl_data = curs.fetchall()
-        if acl_data:
-            if acl_data[0][0] == 'user':
-                if ip_or_user(ip) == 1:
-                    return 1
-
-            if acl_data[0][0] == '50_edit':
-                if ip_or_user(ip) == 1:
-                    return 1
-                
-                if admin_check(5, 'view (' + name + ')') != 1:
-                    curs.execute("select count(title) from history where ip = ?", [ip])
-                    count = curs.fetchall()
-                    if count:
-                        count = count[0][0]
-                    else:
-                        count = 0
-
-                    if count < 50:
-                        return 1
-
-            if acl_data[0][0] == 'admin':
-                if ip_or_user(ip) == 1:
-                    return 1
-
-                if admin_check(5, 'view (' + name + ')') != 1:
-                    return 1
-
-        return 0
-    else:
-        if ban_check() == 1:
-            return 1
+    if ban_check() == 1:
+        return 1
 
+    if tool != 'topic' and tool != 'render':
         acl_c = re.search("^user:((?:(?!\/).)*)", name)
         if acl_c:
             acl_n = acl_c.groups()
@@ -726,9 +739,32 @@ def acl_check(name, tool = ''):
         if re.search("^file:", name) and admin_check(None, 'file edit (' + name + ')') != 1:
             return 1
 
-        curs.execute("select decu from acl where title = ?", [name])
-        acl_data = curs.fetchall()
-        if acl_data:
+    for i in range(0, (2 if tool != 'render' else 1)):
+        if tool == '':
+            if i == 0:
+                curs.execute("select decu from acl where title = ?", [name])
+                acl_data = curs.fetchall()
+            else:
+                curs.execute('select data from other where name = "edit"')
+                acl_data = curs.fetchall()
+
+            num = 5
+        elif tool == 'topic':
+            if i == 0:
+                curs.execute("select dis from acl where title = ?", [name])
+                acl_data = curs.fetchall()
+            else:
+                curs.execute('select data from other where name = "discussion"')
+                acl_data = curs.fetchall()
+
+            num = 3
+        else:
+            curs.execute("select view from acl where title = ?", [name])
+            acl_data = curs.fetchall()
+
+            num = 5
+
+        if acl_data and acl_data[0][0] != 'normal':
             if acl_data[0][0] == 'user':
                 if ip_or_user(ip) == 1:
                     return 1
@@ -737,14 +773,14 @@ def acl_check(name, tool = ''):
                 if ip_or_user(ip) == 1:
                     return 1
 
-                if admin_check(5) != 1:
+                if admin_check(num) != 1:
                     return 1
 
             if acl_data[0][0] == '50_edit':
                 if ip_or_user(ip) == 1:
                     return 1
                 
-                if admin_check(5) != 1:
+                if admin_check(num) != 1:
                     curs.execute("select count(title) from history where ip = ?", [ip])
                     count = curs.fetchall()
                     if count:
@@ -759,42 +795,23 @@ def acl_check(name, tool = ''):
                 if ip_or_user(ip) == 1:
                     return 1
                 
-                if admin_check(5) != 1:
+                if admin_check(num) != 1:
                     curs.execute("select data from user_set where id = ? and name = 'email'", [ip])
                     email = curs.fetchall()
                     if not email:
                         return 1
 
-        curs.execute('select data from other where name = "edit"')
-        set_data = curs.fetchall()
-        if set_data:
-            if set_data[0][0] == 'login':
-                if ip_or_user(ip) == 1:
+            if acl_data[0][0] == 'owner':
+                if admin_check() != 1:
                     return 1
 
-            if set_data[0][0] == 'admin':
-                if ip_or_user(ip) == 1:
-                    return 1
-
-                if admin_check(5, 'edit (' + name + ')') != 1:
-                    return 1
-
-            if set_data[0][0] == '50_edit':
-                if ip_or_user(ip) == 1:
+        if tool == 'topic':
+            curs.execute("select title from rd where title = ? and sub = ? and not stop = ''", [name, sub])
+            if curs.fetchall():
+                if admin_check(3, 'topic (' + name + ')') != 1:
                     return 1
-                
-                if admin_check(5, 'edit (' + name + ')') != 1:
-                    curs.execute("select count(title) from history where ip = ?", [ip])
-                    count = curs.fetchall()
-                    if count:
-                        count = count[0][0]
-                    else:
-                        count = 0
 
-                    if count < 50:
-                        return 1
-
-        return 0
+    return 0
 
 def ban_check(ip = None, tool = None):
     if not ip:
@@ -841,60 +858,7 @@ def ban_check(ip = None, tool = None):
     return 0
         
 def topic_check(name, sub):
-    ip = ip_check()
-
-    if ban_check() == 1:
-        return 1
-
-    curs.execute('select data from other where name = "discussion"')
-    acl_data = curs.fetchall()
-    if acl_data:
-        if acl_data[0][0] == 'login':
-            if ip_or_user(ip) == 1:
-                return 1
-
-        if acl_data[0][0] == 'admin':
-            if ip_or_user(ip) == 1:
-                return 1
-
-            if admin_check(3, 'topic (' + name + ')') != 1:
-                return 1
-
-    curs.execute("select dis from acl where title = ?", [name])
-    acl_data = curs.fetchall()
-    if acl_data:
-        if acl_data[0][0] == 'user':
-            if ip_or_user(ip) == 1:
-                return 1
-
-        if acl_data[0][0] == '50_edit':
-            if ip_or_user(ip) == 1:
-                return 1
-            
-            if admin_check(3, 'topic (' + name + ')') != 1:
-                curs.execute("select count(title) from history where ip = ?", [ip])
-                count = curs.fetchall()
-                if count:
-                    count = count[0][0]
-                else:
-                    count = 0
-
-                if count < 50:
-                    return 1
-
-        if acl_data[0][0] == 'admin':
-            if ip_or_user(ip) == 1:
-                return 1
-
-            if admin_check(3, 'topic (' + name + ')') != 1:
-                return 1
-        
-    curs.execute("select title from rd where title = ? and sub = ? and not stop = ''", [name, sub])
-    if curs.fetchall():
-        if admin_check(3, 'topic (' + name + ')') != 1:
-            return 1
-
-    return 0
+    return acl_check(name, 'topic', sub)
 
 def ban_insert(name, end, why, login, blocker, type_d = None):
     now_time = get_time()
@@ -990,7 +954,7 @@ def number_check(data):
 
 def edit_filter_do(data):
     if admin_check(1) != 1:
-        curs.execute("select regex, sub from filter")
+        curs.execute("select regex, sub from filter where regex != ''")
         for data_list in curs.fetchall():
             match = re.compile(data_list[0], re.I)
             if match.search(data):
@@ -1036,8 +1000,8 @@ def re_error(data):
                 g_regex = re.compile(test_r[1])
                 if g_regex.search(ip):
                     end += '<li>' + load_lang('type') + ' : regex ban</li>'
-                    end += '<li>' + load_lang('end') + ' : ' + test_r[2] + '</li>'
-                    if test_r[0] != 'O':
+                    end += '<li>' + load_lang('end') + ' : ' + (test_r[2] if test_r[2] != '' else load_lang('limitless')) + '</li>'
+                    if test_r[0] == 'O':
                         end += '<li>' + load_lang('login_able') + ' (' + load_lang('not_sure') + ')</li>'
 
                     end += '<hr class=\"main_hr\">'
@@ -1046,8 +1010,8 @@ def re_error(data):
             band_d = curs.fetchall()
             if band_d:
                 end += '<li>' + load_lang('type') + ' : band ban</li>'
-                end += '<li>' + load_lang('end') + ' : ' + band_d[0][1] + '</li>'
-                if data[0][0] != 'O':
+                end += '<li>' + load_lang('end') + ' : ' + (band_d[0][1] if band_d[0][1] != '' else load_lang('limitless')) + '</li>'
+                if band_d[0][0] == 'O':
                     end += '<li>' + load_lang('login_able') + ' (' + load_lang('not_sure') + ')</li>'
 
                 end += '<hr class=\"main_hr\">'
@@ -1056,8 +1020,8 @@ def re_error(data):
             ban_d = curs.fetchall()
             if ban_d:
                 end += '<li>' + load_lang('type') + ' : ban</li>'
-                end += '<li>' + load_lang('end') + ' : ' + ban_d[0][1] + '</li>'
-                if data[0][0] != 'O':
+                end += '<li>' + load_lang('end') + ' : ' + (ban_d[0][1] if ban_d[0][1] != '' else load_lang('limitless')) + '</li>'
+                if ban_d[0][0] == 'O':
                     end += '<li>' + load_lang('login_able') + ' (' + load_lang('not_sure') + ')</li>'
 
                 end += '<hr class=\"main_hr\">'

+ 1 - 1
route/tool/init.py

@@ -29,7 +29,7 @@ server_set_var = {
         'display' : 'Markup',
         'require' : 'select',
         'default' : 'namumark',
-        'list' : ['namumark', 'markdown']
+        'list' : ['namumark', 'markdown', 'raw']
     },
     'encode' : {
         'display' : 'Encryption method',

+ 8 - 3
route/tool/mark.py

@@ -40,13 +40,15 @@ def plusing(data):
         if not curs.fetchall():
             curs.execute("insert into back (title, link, type) values (?, ?, ?)", [data_in[1], data_in[0], data_in[2]])
 
-def namumark(title = '', data = None, num = 0):
+def namumark(title, data, num, include):
     curs.execute('select data from other where name = "markup"')
     rep_data = curs.fetchall()
     if rep_data[0][0] == 'namumark':
-        data = namu(conn, data, title, num)
+        data = namu(conn, data, title, num, include)
     elif rep_data[0][0] == 'markdown':
         data = markdown(conn, data, title, num)
+    elif rep_data[0][0] == 'raw':
+        data = [data, '', []]
     else:
         data = ['', '', []]
 
@@ -68,4 +70,7 @@ def namumark(title = '', data = None, num = 0):
         
         conn.commit()
         
-    return data[0] + data[1]
+    if num == 2:
+        return [data[0], data[1]]
+    else:
+        return data[0] + data[1]

+ 91 - 46
route/tool/set_mark/namu.py

@@ -79,11 +79,17 @@ def table_parser(data, cel_data, start_data, num = 0):
         
     cel_width = re.search("&lt;width=((?:(?!&gt;).)*)&gt;", data)
     if cel_width:
-        cel_style += 'width: ' + cel_width.groups()[0] + 'px;'
+        if re.search('^[0-9]+$', cel_width.groups()[0]):
+            cel_style += 'width: ' + cel_width.groups()[0] + 'px;'
+        else:
+            cel_style += 'width: ' + cel_width.groups()[0] + ';'
 
     cel_height = re.search("&lt;height=((?:(?!&gt;).)*)&gt;", data)
     if cel_height:
-        cel_style += 'height: ' + cel_height.groups()[0] + 'px;'
+        if re.search('^[0-9]+$', cel_height.groups()[0]):
+            cel_style += 'height: ' + cel_height.groups()[0] + 'px;'
+        else:
+            cel_style += 'height: ' + cel_height.groups()[0] + ';'
         
     text_right = re.search("&lt;\)&gt;", data)
     text_center = re.search("&lt;:&gt;", data)
@@ -161,13 +167,14 @@ def table_start(data):
             
     return data
 
-def middle_parser(data, fol_num, syntax_num, folding_num):
+def middle_parser(data, fol_num, syntax_num, folding_num, include_num):
     global end_data
     global plus_data
 
     middle_stack = 0
     middle_list = []
     middle_number = 0
+    html_number = 0
 
     middle_re = re.compile('(?:{{{((?:(?:(?! |{{{|}}}|&lt;).)*) ?)|(}}}))')
     while 1:
@@ -259,13 +266,22 @@ def middle_parser(data, fol_num, syntax_num, folding_num):
                                                         if folding_num == 0:
                                                             folding_num = 1
                                                         
-                                                        data = re.sub('{{{#!folding ?((?:(?!\n).)*)\n?', '<div>' + str(folding_data[0]) + ' <div style="display: inline-block;"><a href="javascript:void(0);" onclick="folding(' + str(fol_num) + ');">[+]</a></div_end><div id="folding_' + str(fol_num) + '" style="display: none;"><div id="wiki_div" style="">', data, 1)
+                                                        data = re.sub('{{{#!folding ?((?:(?!\n).)*)\n?', '<div>' + str(folding_data[0]) + ' <div style="display: inline-block;"><a href="javascript:void(0);" onclick="do_open_folding(' + str(fol_num) + ', \'' + include_num + '\');">[+]</a></div_end><div id="' + include_num + 'folding_' + str(fol_num) + '" style="display: none;"><div id="wiki_div" style="">\n', data, 1)
                                                         
                                                         fol_num += 1
+
                                                     else:
-                                                        middle_list += ['span']
+                                                        middle_search = re.search('^#!html', middle_data[0])
+                                                        if middle_search:
+                                                            middle_list += ['div_end']
+                                                            
+                                                            html_number += 1
+                                                        
+                                                            data = middle_re.sub('<div id="' + include_num + 'render_contect_' + str(html_number) + '">', data, 1)
+                                                        else:
+                                                            middle_list += ['span']
 
-                                                        data = middle_re.sub('<span>', data, 1)
+                                                            data = middle_re.sub('<span>', data, 1)
                     else:
                         middle_list += ['code']
                         
@@ -350,7 +366,7 @@ def middle_parser(data, fol_num, syntax_num, folding_num):
 
     return [data, [fol_num, syntax_num, folding_num]]
 
-def namu(conn, data, title, main_num):
+def namu(conn, data, title, main_num, include_num):
     curs = conn.cursor()
 
     global plus_data
@@ -362,16 +378,21 @@ def namu(conn, data, title, main_num):
     backlink = []
     end_data = []
     
+    include_num = include_num + '-' if include_num else ''
+    
     data = re.sub('<math>(?P<in>(?:(?!<\/math>).)+)<\/math>', '[math(\g<in>)]', data)
     data = html.escape(data)
 
     data = re.sub('\r\n', '\n', data)
 
-    t_data = middle_parser(data, 0, 0, 0)
+    t_data = middle_parser(data, 0, 0, 0, include_num)
     data = t_data[0]
 
     include_re = re.compile('\[include\(((?:(?!\)\]).)+)\)\]', re.I)
+    i = 0
     while 1:
+        i += 1
+
         include = include_re.search(data)
         if include:
             include = include.groups()[0]
@@ -386,41 +407,22 @@ def namu(conn, data, title, main_num):
 
             backlink += [[title, include_link, 'include']]
 
-            include = re.sub('^((?:(?!,).)+)', '', include)
-            
-            num = 0
-            while 1:
-                include_one_nowiki = re.search('(?:\\\\){2}(.)', include)
-                if include_one_nowiki:
-                    include_one_nowiki = include_one_nowiki.groups()
-
-                    num += 1
-
-                    end_data += [['include_one_nowiki_' + str(num), include_one_nowiki[0], 'normal']]
-
-                    include = re.sub('(?:\\\\){2}(.)', '<span id="include_one_nowiki_' + str(num) + '"></span>', include, 1)
-                else:
-                    break
-
-            curs.execute("select data from data where title = ?", [include_data])
-            include_data = curs.fetchall()
-            if include_data:
-                include_parser = include_re.sub('', include_data[0][0])
-                include_parser = html.escape(include_parser)
+            curs.execute("select title from data where title = ?", [include_data])
+            if curs.fetchall():
+                data = include_re.sub('<div id="include_' + str(i) + '"></div>', data, 1)
 
+                include_plus_data = []
                 while 1:
                     include_plus = re.search(', ?((?:(?!=).)+)=((?:(?!,).)+)', include)
                     if include_plus:
                         include_plus = include_plus.groups()
-                        include_parser = include_parser.replace('@' + include_plus[0] + '@', include_plus[1])
+                        include_plus_data += [[include_plus[0], include_plus[1]]]
 
                         include = re.sub(', ?((?:(?!=).)+)=((?:(?!,).)+)', '', include, 1)
                     else:
                         break
 
-                include_parser = re.sub('\[\[(?:category|분류):(((?!\]\]|#include).)+)\]\]', '', include_parser)
-
-                data = include_re.sub('<include><a id="include_link" href="/w/' + tool.url_pas(include_link) + '">\n[' + include_link + ']</a>\n' + include_parser + '\n</include>', data, 1)
+                plus_data += '<script>load_include("' + include_link + '", "include_' + str(i) + '", ' + str(include_plus_data) + ');</script>'
             else:
                 data = include_re.sub('<a id="not_thing" href="/w/' + tool.url_pas(include_link) + '">' + include_link + '</a>', data, 1)
         else:
@@ -428,7 +430,7 @@ def namu(conn, data, title, main_num):
 
     data = re.sub('\r\n', '\n', data)
 
-    t_data = middle_parser(data, t_data[1][0], t_data[1][1], t_data[1][2])
+    t_data = middle_parser(data, t_data[1][0], t_data[1][1], t_data[1][2], include_num)
     data = t_data[0]
 
     data = re.sub('&amp;', '&', data)
@@ -437,6 +439,7 @@ def namu(conn, data, title, main_num):
     data = re.sub('\|\|( +)\n', '||\n', data)
 
     data = re.sub('\n##(((?!\n).)+)', '', data)
+    data = re.sub('<div id="wiki_div" style="">\n', '<div id="wiki_div" style="">', data)
            
     while 1:
         wiki_table_data = re.search('<div id="wiki_div" ((?:(?!>).)+)>((?:(?!<div id="wiki_div"|<\/div_end>).\n*)+)<\/div_end>', data)
@@ -471,7 +474,7 @@ def namu(conn, data, title, main_num):
                         "''' + math.replace('\\', '\\\\').replace('&lt;', '<').replace('&gt;', '>') + '''",
                         document.getElementById("math_''' + str(first) + '''")
                     );
-            </script>
+                </script>
             '''
         else:
             break
@@ -529,7 +532,7 @@ def namu(conn, data, title, main_num):
     edit_number = 0
     toc_data = '<div id="toc"><span id="toc_title">TOC</span>\n\n'
     while 1:
-        toc = re.search('\n(={1,6}) ?((?:(?!\n).)+) ?\n', data)
+        toc = re.search('\n(={1,6}) ?((?:(?!\n).)+) ?(?:={1,6})\n', data)
         if toc:
             toc = toc.groups()
             
@@ -564,7 +567,7 @@ def namu(conn, data, title, main_num):
             toc_main_data = toc[1]
             toc_main_data = re.sub('=*$', '', toc_main_data)
             toc_main_data = re.sub('\[\*((?:(?! |\]).)*)(?: ((?:(?!(\[\*(?:(?:(?!\]).)+)\]|\])).)+))?\]', '', toc_main_data)
-            toc_main_data = re.sub('<span id="math_[0-9]"><\/span>', '(수식)', toc_main_data)
+            toc_main_data = re.sub('<span id="math_[0-9]"><\/span>', '(Math)', toc_main_data)
             
             toc_data += '<span style="margin-left: ' + str((toc_full - toc_top_stack) * 10) + 'px;"><a href="#s-' + re.sub('\.$', '', all_stack) + '">' + all_stack + '</a> ' + toc_main_data + '</span>\n'
         else:
@@ -586,6 +589,13 @@ def namu(conn, data, title, main_num):
 
     date_re = re.compile('\[date\]', re.I)
     data = date_re.sub(now_time, data)
+
+    pagecount_re = re.compile('\[pagecount\]', re.I)
+
+    curs.execute('select data from other where name = "count_all_title"')
+    all_title = curs.fetchall()
+
+    data = pagecount_re.sub(all_title[0][0], data)
     
     time_data = re.search('^([0-9]{4}-[0-9]{2}-[0-9]{2})', now_time)
     time = time_data.groups()
@@ -801,9 +811,8 @@ def namu(conn, data, title, main_num):
                 else:
                     data = re.sub('\[\[((?:(?!\[\[|\]\]).)+)\]\]', '<a id="not_thing" href="/w/' + tool.url_pas(file_alt) + '">' + file_alt + '</a>', data, 1)
             elif category_re.search(main_link):
-                see_link = re.sub('#include', '', see_link)
-                main_link = re.sub('#include', '', category_re.sub('category:', main_link))
-
+                main_link = category_re.sub('category:', main_link)
+                
                 if re.search('#blur', main_link):
                     see_link = 'Hidden'
                     link_id = 'id="inside"'
@@ -843,7 +852,7 @@ def namu(conn, data, title, main_num):
                 data = re.sub('\[\[((?:(?!\[\[|\]\]).)+)\]\]', '<a id="out_link" rel="nofollow" href="' + main_link + '">' + see_link + '</a>', data, 1)
             else:
                 return_link = tool.link_fix(main_link)
-                main_link = return_link[0]
+                main_link = html.unescape(return_link[0])
                 other_link = return_link[1]
 
                 if re.search('^\/', main_link):
@@ -904,7 +913,15 @@ def namu(conn, data, title, main_num):
                     else:
                         footdata_in = footdata[2]
 
-                    footdata_all += '<li><a href="#rfn-' + str(footdata[0]) + '">(' + footdata[1] + ')</a> <span id="fn-' + str(footdata[0]) + '">' + footdata_in + '</span></li>'
+                    footdata_all += '' + \
+                        '<li>' + \
+                            '<a href="#' + include_num + 'rfn-' + str(footdata[0]) + '" ' + \
+                                'id="' + include_num + 'cfn-' + str(footdata[0]) + '" ' + \
+                                'onclick="do_open_foot(\'' + include_num + 'fn-' + str(footdata[0]) + '\', 1);">' + \
+                                '(' + footdata[1] + ')' + \
+                            '</a> <span id="' + include_num + 'fn-' + str(footdata[0]) + '">' + footdata_in + '</span>' + \
+                        '</li>' + \
+                    ''
                 
                 data = re_footnote.sub(footdata_all + '</ul>', data, 1)
                 
@@ -922,9 +939,17 @@ def namu(conn, data, title, main_num):
 
                         footnote_all += [[float(footshort), footshort, 0]]
 
-                        data = re_footnote.sub('<sup><a href="javascript:open_foot(\'fn-' + footshort + '\')" id="rfn-' + footshort + '">(' + footnote_name + ')</a></sup><span class="foot_plus" id="cfn-' + footshort + '"></span>', data, 1)
+                        data = re_footnote.sub('' + \
+                            '<sup>' + \
+                                '<a href="#' + include_num + 'fn-' + footshort + '" ' + \
+                                    'id="' + include_num + 'rfn-' + footshort + '" ' + \
+                                    'onclick="do_open_foot(\'' + include_num + 'fn-' + footshort + '\');">' + \
+                                    '(' + footnote_name + ')' + \
+                                '</a>' + \
+                            '</sup>' + \
+                        '', data, 1)
                     else:
-                        data = re_footnote.sub('<sup><a href="#">(' + footnote_name + ')</a></sup>', data, 1)
+                        data = re_footnote.sub('<sup><a href="javascript:void(0);">(' + footnote_name + ')</a></sup>', data, 1)
                 else:
                     footnote_number += 1
 
@@ -940,7 +965,15 @@ def namu(conn, data, title, main_num):
 
                     footnote_all += [[footnote_number, footnote_name, footnote]]
                     
-                    data = re_footnote.sub('<sup><a href="javascript:open_foot(\'fn-' + str(footnote_number) + '\')" id="rfn-' + str(footnote_number) + '">(' + footnote_name + ')</a></sup><span class="foot_plus" id="cfn-' + str(footnote_number) + '"></span>', data, 1)
+                    data = re_footnote.sub('' + \
+                        '<sup>' + \
+                            '<a href="#' + include_num + 'fn-' + str(footnote_number) + '" ' + \
+                                'id="' + include_num + 'rfn-' + str(footnote_number) + '" ' + \
+                                'onclick="do_open_foot(\'' + include_num + 'fn-' + str(footnote_number) + '\');">' + \
+                                '(' + footnote_name + ')' + \
+                            '</a>' + \
+                        '</sup>' + \
+                    '', data, 1)
         else:
             break
 
@@ -954,7 +987,15 @@ def namu(conn, data, title, main_num):
         else:
             footdata_in = footdata[2]
 
-        footdata_all += '<li><a href="#rfn-' + str(footdata[0]) + '">(' + footdata[1] + ')</a> <span id="fn-' + str(footdata[0]) + '">' + footdata_in + '</span></li>'
+        footdata_all += '' + \
+            '<li>' + \
+                '<a href="#' + include_num + 'rfn-' + str(footdata[0]) + '" ' + \
+                    'id="' + include_num + 'cfn-' + str(footdata[0]) + '" ' + \
+                    'onclick="do_open_foot(\'' + include_num + 'fn-' + str(footdata[0]) + '\', 1);">' + \
+                    '(' + footdata[1] + ')' + \
+                '</a> <span id="' + include_num + 'fn-' + str(footdata[0]) + '">' + footdata_in + '</span>' + \
+            '</li>' + \
+        ''
 
     footdata_all += '</ul>'
     if footdata_all == '<hr><ul id="footnote_data"></ul>':
@@ -1032,5 +1073,9 @@ def namu(conn, data, title, main_num):
     data = re.sub('\n<\/ul>', '</ul>', data)
 
     data = re.sub('\n', '<br>', data)
+    if include_num == '':
+        plus_data = '<script>render_html();</script>' + plus_data
+    else:
+        plus_data = '<script>render_html(\'' + include_num + '\');</script>' + plus_data
 
     return [data, plus_data, backlink]

+ 2 - 3
route/topic.py

@@ -80,8 +80,7 @@ def topic_2(conn, name, sub):
     
         curs.execute("select stop from rd where title = ? and sub = ? and stop != ''", [name, sub])
         close_data = curs.fetchall()
-        
-        if close_data and admin != 1:
+        if (close_data and admin != 1) or topic_check(name, sub) == 1:
             display = 'display: none;'
         else:
             display = ''
@@ -107,4 +106,4 @@ def topic_2(conn, name, sub):
                 <h2 id="topic_top_title">''' + sub + '''</h2>
                 ''' + data,
             menu = [['topic/' + url_pas(name), load_lang('list')]]
-        ))
+        ))

+ 0 - 2
route/topic_tool.py

@@ -24,8 +24,6 @@ def topic_tool_2(conn, name, sub):
                 <li><a href="/topic/''' + url_pas(name) + '/sub/' + url_pas(sub) + '''/setting"> Topic setting</a></li>
             </ul>
         '''
-
-    print(close_data)
     data += '''
         <h2>''' + load_lang('tool') + '''</h2>
         <ul>

+ 2 - 2
route/user_custom_head_view.py

@@ -6,7 +6,7 @@ def user_custom_head_view_2(conn):
     ip = ip_check()
 
     if flask.request.method == 'POST':
-        if custom()[2] != 0:
+        if ip_or_user(ip) == 0:
             curs.execute("select user from custom where user = ?", [ip + ' (head)'])
             if curs.fetchall():
                 curs.execute("update custom set css = ? where user = ?", [flask.request.form.get('content', None), ip + ' (head)'])
@@ -19,7 +19,7 @@ def user_custom_head_view_2(conn):
 
         return redirect('/user')
     else:
-        if custom()[2] != 0:
+        if ip_or_user(ip) == 0:
             start = ''
 
             curs.execute("select css from custom where user = ?", [ip + ' (head)'])

+ 1 - 1
route/user_info.py

@@ -11,7 +11,7 @@ def user_info_2(conn):
     else:
         plus2 = '<li><a href="/alarm">' + load_lang('alarm') + '</a></li>'
 
-    if custom()[2] != 0:        
+    if ip_or_user(ip) == 0:  
         plus = '''
             <li><a href="/logout">''' + load_lang('logout') + '''</a></li>
             <li><a href="/change">''' + load_lang('user_setting') + '''</a></li>

+ 5 - 10
route/user_setting.py

@@ -4,17 +4,12 @@ def user_setting_2(conn, server_init):
     curs = conn.cursor()
 
     support_language = server_init.server_set_var['language']['list']
+    ip = ip_check()
 
     if ban_check() == 1:
         return re_error('/ban')
 
-    if custom()[2] == 0:
-        return redirect('/login')
-
-    ip = ip_check()
-    user_state = flask.request.args.get('user', 'ip')
-    
-    if user_state == 'ip':
+    if ip_or_user(ip) == 0:
         if flask.request.method == 'POST':    
             auto_list = ['email', 'skin', 'lang']
 
@@ -60,9 +55,9 @@ def user_setting_2(conn, server_init):
                 curs.execute('select name, picture from oauth_conn where wiki_id = ? and provider = ?', [flask.session['id'], oauth_provider[i]])
                 oauth_data = curs.fetchall()
                 if len(oauth_data) == 1:
-                    oauth_content += '<li>{} - {}</li>'.format(oauth_provider[i].capitalize(), load_lang('connection') + ' : <img src="{}" width="17px" height="17px">{}'.format(oauth_data[0][1], oauth_data[0][0]))
+                    oauth_content += '<li>{}</li>'.format(oauth_provider[i].capitalize() + ' : <img src="{}" width="17px" height="17px"> {}'.format(oauth_data[0][1], oauth_data[0][0]))
                 else:
-                    oauth_content += '<li>{} - {}</li>'.format(oauth_provider[i].capitalize(), load_lang('connection') + ' : <a href="/oauth/{}/init">{}</a>'.format(oauth_provider[i], load_lang('connect')))
+                    oauth_content += '<li>{}</li>'.format(oauth_provider[i].capitalize() + ' <a href="/oauth/{}/init">({})</a>'.format(oauth_provider[i], load_lang('connect')))
             
             oauth_content += '</ul>'
 
@@ -96,4 +91,4 @@ def user_setting_2(conn, server_init):
                 menu = [['user', load_lang('return')]]
             ))
     else:
-        pass
+        return redirect('/login')

+ 10 - 3
route/view_read.py

@@ -22,6 +22,9 @@ def view_read_2(conn, name):
     curs.execute("select sub from rd where title = ? and not stop = 'O' order by date desc", [name])
     if curs.fetchall():
         sub += ' (' + load_lang('discussion') + ')'
+        topic = 1
+    else:
+        topic = 0
 
     curs.execute("select link from back where title = ? and type = 'cat' order by link asc", [name])
                 
@@ -141,13 +144,13 @@ def view_read_2(conn, name):
         else:
             menu = [['edit/' + url_pas(name), load_lang('edit')]]
 
-        menu += [['topic/' + url_pas(name), load_lang('discussion')], ['history/' + url_pas(name), load_lang('history')], ['xref/' + url_pas(name), load_lang('backlink')], ['acl/' + url_pas(name), load_lang('acl')]]
+        menu += [['topic/' + url_pas(name), load_lang('discussion'), topic], ['history/' + url_pas(name), load_lang('history')], ['xref/' + url_pas(name), load_lang('backlink')], ['acl/' + url_pas(name), load_lang('acl')]]
 
         if flask.request.args.get('from', None):
             menu += [['w/' + url_pas(name), load_lang('pass')]]
             end_data = '''
                 <div id="redirect">
-                    <a href="/w/''' + url_pas(flask.request.args.get('from', None)) + '?from=' + url_pas(name) + '">' + flask.request.args.get('from', None) + '</a> → ' + name + '''
+                    <a href="/w/''' + url_pas(flask.request.args.get('from', None)) + '?from=' + url_pas(name) + '">' + flask.request.args.get('from', None) + '</a> ⇨ <b>' + name + '''</b>
                 </div>
                 <br>
             ''' + end_data
@@ -181,10 +184,14 @@ def view_read_2(conn, name):
     body = curs.fetchall()
     if body:
         div = body[0][0] + '<hr class=\"main_hr\">' + div
+        
+    curs.execute("select data from other where name = 'bottom_body'")
+    body = curs.fetchall()
+    if body:
+        div += '<hr class=\"main_hr\">' + body[0][0]
     
     div = adsense_code + '<div>' + div + '</div>'
 
-    # 이 부분 개선 필요
     match = re.search("^user:([^/]*)", name)
     if match:
         user_name = match.groups()[0]

+ 3 - 4
route/watch_list.py

@@ -4,11 +4,10 @@ def watch_list_2(conn):
     curs = conn.cursor()
 
     div = 'Limit : 10<hr class=\"main_hr\">'
-    
-    if custom()[2] == 0:
-        return redirect('/login')
+    ip = ip_check()    
 
-    ip = ip_check()
+    if ip_or_user(ip) != 0:
+        return redirect('/login')
 
     curs.execute("delete from scan where user = ? and title = ''", [ip])
     conn.commit()

+ 2 - 3
route/watch_list_name.py

@@ -3,10 +3,9 @@ from .tool.func import *
 def watch_list_name_2(conn, name):
     curs = conn.cursor()
     
-    if custom()[2] == 0:
-        return redirect('/login')
-
     ip = ip_check()
+    if ip_or_user(ip) != 0:
+        return redirect('/login')
 
     curs.execute("select count(title) from scan where user = ?", [ip])
     count = curs.fetchall()

+ 2 - 2
version.json

@@ -1,10 +1,10 @@
 {
     "master" : {
-        "r_ver" : "v3.1.2-stable-03",
+        "r_ver" : "v3.1.3-stable-01",
         "c_ver" : "400003",
         "s_ver" : "2"
     }, "stable" : {
-        "r_ver" : "v3.1.2-stable-03",
+        "r_ver" : "v3.1.3-stable-01",
         "c_ver" : "400003",
         "s_ver" : "2"
     }

+ 6 - 3
views/main_css/css/main.css

@@ -32,10 +32,13 @@ s:hover, strike:hover, del:hover { color: gray; background-color: gainsboro; tex
 #main_table_width_quarter { width: 25%; }
 #redirect { border: 1px solid; padding: 10px; }
 body { word-break: break-all; overflow: auto; }
-hr.main_hr { border: none; }
+hr.main_hr { border: none; margin-top: 8px; margin-bottom: 8px; }
 #include_link { display: none; }
-.foot_plus { background: gainsboro; }
+.foot_plus { background: white; border: 5px solid gainsboro; padding: 5px; width: 300px; height: 300px; position: fixed; top: calc(50% - 150px); right: calc(50% - 150px); display: none; z-index: 100; }
 #toc_title { font-size: 18px; }
 blockquote { background-image: url(/views/acme/img/quote.png); background-position: calc(100% - 10px) 10px; background-repeat: no-repeat; background-size: 25px; }
 #admin_log_search { width: 100px; }
-@media (max-width: 768px) { table { min-width: 100%; }}
+@media (max-width: 768px) { table { min-width: 100%; }}
+@media (max-width: 400px) { .foot_plus { width: 80%; right: calc(100% - 92%); }}
+@media (max-height: 400px) { .foot_plus { height: 80%; top: calc(100% - 92%); }}
+.foot_in { overflow-y: scroll; height: calc(100% - 20px); }

+ 1 - 1
views/main_css/js/insert_data.js → views/main_css/js/do_insert_data.js

@@ -1,6 +1,6 @@
 // https://stackoverflow.com/questions/11076975/insert-text-into-textarea-at-cursor-position-javascript
 
-function insert_data(name, data) {
+function do_insert_data(name, data) {
     if(document.selection) { 
         document.getElementById(name).focus();
 

+ 2 - 2
views/main_css/js/folding.js → views/main_css/js/do_open_folding.js

@@ -1,5 +1,5 @@
-function folding(num) { 
-    var fol = document.getElementById('folding_' + num); 
+function do_open_folding(num, include_num) { 
+    var fol = document.getElementById(include_num + 'folding_' + num); 
     if(fol.style.display === 'inline-block' || fol.style.display === 'block') { 
         fol.style.display = 'none';
     } else {

+ 21 - 0
views/main_css/js/do_open_foot.js

@@ -0,0 +1,21 @@
+function do_open_foot(name, num = 0) {
+    var found_include = name.match(/^(include_(?:[0-9]+)\-)/);
+    if(found_include) {
+        var include_name = name.replace(/^(?:include_(?:[0-9]+)\-)/, '');
+        document.getElementById(found_include[1] + 'r' + include_name).style.color = 'red';
+        if(num === 1) {
+            document.getElementById(found_include[1] + 'c' + include_name).style.color = 'inherit';
+        } else {
+            document.getElementById(found_include[1] + 'c' + include_name).style.color = 'red';
+        }
+    } else {
+        document.getElementById('r' + name).style.color = 'red';
+        if(num === 1) {
+            document.getElementById('c' + name).style.color = 'inherit';
+        } else {
+            document.getElementById('c' + name).style.color = 'red';
+        }
+    }
+
+    
+}

+ 22 - 0
views/main_css/js/dummy_do_open_foot.js

@@ -0,0 +1,22 @@
+function dummy_do_open_foot(name) {
+    var found_include = name.match(/^(include_(?:[0-9]+)\-)/);
+    if(found_include) {
+        var o_data = document.getElementById(found_include[1] + 'c' + name.replace(/^(?:include_(?:[0-9]+)\-)/, ''));
+    } else {
+        var o_data = document.getElementById('c' + name);
+    }
+    
+    var g_data = document.getElementById(name.replace(/\.([0-9]+)$/, ''));
+
+    if(o_data.innerHTML === '') {
+        o_data.style.display = 'block';
+        o_data.innerHTML += '' + 
+            '<div class="foot_in">' + 
+                '<a onclick="do_open_foot(\'' + name + '\')" href="javascript:void(0);">(X)</a> <a onclick="do_open_foot(\'' + name + '\')" href="#' + name + '">(Go)</a> ' + g_data.innerHTML + 
+            '</div>' + 
+        '';
+    } else {
+        o_data.style.display = 'none';
+        o_data.innerHTML = '';
+    }
+}

+ 33 - 0
views/main_css/js/load_include.js

@@ -0,0 +1,33 @@
+function load_include(title, name, p_data) {
+    var o_data = document.getElementById(name);
+
+    var url = "/api/w/" + encodeURI(title) + "?include=" + name;
+    
+    var xhr = new XMLHttpRequest();
+    xhr.open("GET", url, true);
+    xhr.send(null);
+
+    xhr.onreadystatechange = function() {
+        if(xhr.readyState === 4 && xhr.status === 200) {
+            var o_p_data = JSON.parse(xhr.responseText);
+            var g_data = o_p_data['data'];
+            
+            for(key in p_data) {
+                try {
+                    var patt = new RegExp('@' + p_data[key][0] + '@', 'g');
+                    g_data = g_data.replace(patt, p_data[key][1]);
+                } catch(e) {
+                    console.log(e);
+                }
+            }
+
+            o_data.innerHTML = g_data;
+
+            js_data = o_p_data['js_data'];
+            js_data = js_data.replace(/<script>/g, '');
+            js_data = js_data.replace(/<\/script>/g, '\n');
+
+            eval(js_data)
+        }
+    }
+}

+ 8 - 10
views/main_css/js/load_preview.js

@@ -5,7 +5,7 @@ function load_preview(name) {
     var s_data = new FormData();
     s_data.append('data', o_data.value);
 
-    var url = "/api/w/" + encodeURI(name);
+    var url = "/api/w/" + name;
     var url_2 = "/api/markup";
     
     var xhr = new XMLHttpRequest();
@@ -18,17 +18,15 @@ function load_preview(name) {
 
     xhr.onreadystatechange = function() {
         if(xhr.readyState === 4 && xhr.status === 200) {
-            p_data.innerHTML = JSON.parse(xhr.responseText)['data'];
+            var o_p_data = JSON.parse(xhr.responseText);
 
-            xhr_2.onreadystatechange = function() {
-                if(xhr_2.readyState === 4 && xhr_2.status === 200) {
-                    markup = JSON.parse(xhr_2.responseText)['markup'];
+            p_data.innerHTML = o_p_data['data'];
 
-                    if(markup === 'markdown') {
-                        render_markdown();
-                    }
-                }
-            }
+            js_data = o_p_data['js_data'];
+            js_data = js_data.replace(/<script>/g, '');
+            js_data = js_data.replace(/<\/script>/g, '\n');
+
+            eval(js_data)
         }
     }
 }

+ 0 - 12
views/main_css/js/open_foot.js

@@ -1,12 +0,0 @@
-function open_foot(name) {
-    var o_data = document.getElementById('c' + name);
-    
-    name = name.replace(/\.([0-9]+)$/, '');
-    var g_data = document.getElementById(name);
-
-    if(o_data.innerHTML === '') {
-        o_data.innerHTML += '<sup><a onclick="open_foot(\'' + name + '\')" href="#' + name + '">(Go)</a></sup> ' + g_data.innerHTML;
-    } else {
-        o_data.innerHTML = '';
-    }
-}

+ 33 - 0
views/main_css/js/render_html.js

@@ -0,0 +1,33 @@
+function render_html(name = '') {
+    var num = 0;
+    while(1) {
+        num += 1
+        if(document.getElementById(name + 'render_contect_' + String(num))) {
+            data = document.getElementById(name + 'render_contect_' + String(num)).innerHTML;
+
+            t_data = ['b', 'i', 's', 'del']
+            for(var key in t_data) {
+                var patt = new RegExp('&lt;' + t_data[key] + '&gt;((?:(?!&lt;\/' + t_data[key] + '&gt;).)*)&lt;\/' + t_data[key] + '&gt;', 'ig');
+                data = data.replace(patt, '<' + t_data[key] + '>$1</' + t_data[key] + '>');
+            }
+            
+            src_list = {
+                'www.youtube.com' : '1'
+            }
+            data = data.replace(/&lt;iframe( (?:(?:(?!&gt;).)+))&gt;&lt;\/iframe&gt;/g, function(full, in_data) {
+                src_data = data.match(/ src=['"]https:\/\/([^/'"]+)(?:[^'"]+)['"](?: |$)/);
+                if(src_data) {
+                    if(src_list[src_data[1]]) {
+                        return '<iframe' + in_data + '></iframe>';
+                    } else {
+                        return full;
+                    }
+                }
+            });
+            
+            document.getElementById(name + 'render_contect_' + String(num)).innerHTML = data;
+        } else {
+            break;
+        }
+    }
+}

+ 1 - 1
views/main_css/js/topic_main_load.js

@@ -20,7 +20,7 @@ function topic_main_load(name, sub, s_num) {
     xhr.onreadystatechange = function() {
         if(xhr.readyState === 4 && xhr.status === 200) {
             t_data = JSON.parse(xhr.responseText);
-            for(key in t_data) {
+            for(var key in t_data) {
                 n_data += t_data[key]['data'];
                 num = key;
             }

+ 1 - 1
views/main_css/js/topic_top_load.js

@@ -11,7 +11,7 @@ function topic_top_load(name, sub) {
     xhr.onreadystatechange = function() {
         if(this.readyState === 4 && this.status === 200) {
             t_data = JSON.parse(this.responseText);
-            for(key in t_data) {
+            for(var key in t_data) {
                 n_data += t_data[key]['data'];
                 num = key;
             }

+ 50 - 17
views/neo_yousoro/css/main.css

@@ -49,8 +49,7 @@ li {
 
 #top {
     width: 100%;
-    border-bottom: 2px solid gainsboro;
-    background: skyblue;
+    background: #4a4a4a;
     padding: 15px;
     padding-right: 0;
     padding-left: 0;
@@ -63,17 +62,14 @@ li {
 #main {
     margin: auto;
     margin-top: -20px;
-    padding: 10px;
+    padding: 20px;
     padding-bottom: 20px;
     background: white;
-    border-left: 2px solid gainsboro;
-    border-right: 2px solid gainsboro;
     min-height: 350px;
 }
 
 #bottom {
     width: 100%;
-    border-top: 2px solid gainsboro;
     background: white;
     padding-bottom: 20px;
 }
@@ -91,6 +87,7 @@ li {
     float: right;
     border: 2px solid gainsboro;
     padding: 10px;
+    border-radius: 10px;
 }
 
 @media (min-width: 780px) and (max-width: 1350px) {
@@ -218,6 +215,7 @@ h6 {
 
 #top_tool {
     float: right;
+    color: white;
     margin-top: -5px;
 }
 
@@ -227,6 +225,7 @@ h6 {
     position: absolute;
     background: white;
     margin-top: 10px;
+    border-radius: 10px;
 }
 
 #top_tool_cel {
@@ -244,12 +243,15 @@ h6 {
 
 #search_input {
     width: 80%;
+    border: 1px solid #4a4a4a;
+    border-radius: 10px;
 }
 
 #search button {
     padding: 0;
     border: none;
     background: transparent;
+    color: white;
 }
 
 #mobile_search button {
@@ -283,16 +285,15 @@ h6 {
     margin-top: 5%;
 }
 
-#close_top {
-    float: right;
-    margin-right: 10px;
-}
-
 #toc {
     max-width: 300px;
 }
 
 #top a {
+    color: white;
+}
+
+#top .cel_in_cel a {
     color: black;
 }
 
@@ -303,10 +304,12 @@ a {
 #syntax {
     background: #f0f0f0;
     padding: 5px;
+    border-radius: 10px;
 }
 
 pre {
     border: 2px solid gainsboro;
+    border-radius: 10px;
 }
 
 textarea {
@@ -322,10 +325,12 @@ input {
 
 #toc {
     border: 2px solid gainsboro;
+    border-radius: 10px;
 }
 
 #cate {
     border: 2px solid gainsboro;
+    border-radius: 10px;
 }
 
 #syntax code {
@@ -334,19 +339,18 @@ input {
 
 #redirect {
     border: 2px solid gainsboro;
+    border-radius: 10px;
 }
 
 #go_toc {
     display: inline-block;
     padding: 10px;
-    border-left: 2px solid skyblue;
     width: 25px;
 }
 
 #go_top {
     display: inline-block;
     padding: 10px;
-    border-right: 2px solid skyblue;
     width: 25px;
 }
 
@@ -362,13 +366,14 @@ input {
     position: fixed;
     bottom: 0;
     right: 0;
-    border: 2px solid skyblue;
-    background: white;
+    border: 2px solid #4a4a4a;
+    background: #4a4a4a;
     text-align: center;
+    border-radius: 10px 0px 0px 0px;
 }
 
 #nav_bar a {
-    color: black;
+    color: white;
 }
 
 blockquote {
@@ -376,5 +381,33 @@ blockquote {
     margin: 1em 0em 0em;
     background: #eeeeee;
     border: 2px dashed #ccc;
-    border-left: 5px solid skyblue;
+    border-left: 5px solid black;
+}
+
+#out_link::before {
+    font-family: "Font Awesome 5 Free";
+    content: "\f360";
+    font-weight: 900;
+    background: transparent;
+    color: green;
+}
+
+a:hover {
+    text-decoration: underline;
+}
+
+#mobile_menu a:hover, #top a:hover {
+    text-decoration: none;
+}
+
+h1#title {
+    margin-bottom: 10px;
+}
+
+div#last_edit {
+    margin-bottom: 20px;
+}
+
+#topic_color {
+    color: limegreen;
 }

+ 31 - 17
views/neo_yousoro/index.html

@@ -4,14 +4,14 @@
         <meta charset="utf-8">
         <title>{{imp[0]}} - {{imp[1][0]}}</title>
         {{imp[3][3]|safe}}
-        <link rel="stylesheet" href="/views/neo_yousoro/css/main.css">
-        <script src="/views/neo_yousoro/js/main.js"></script>
-        <script src="/views/neo_yousoro/js/skin_set.js"></script>
+        <link rel="stylesheet" href="/views/neo_yousoro/css/main.css?ver=3">
+        <script src="/views/neo_yousoro/js/main.js?ver=1"></script>
+        <script src="/views/neo_yousoro/js/skin_set.js?ver=1"></script>
         <link   rel="stylesheet"
                 href="https://use.fontawesome.com/releases/v5.7.2/css/all.css"
                 integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr"
                 crossorigin="anonymous">
-        <link rel="shortcut icon" href="/views/main_css/file/favicon.ico">
+        <link rel="shortcut icon" href="/views/main_css/file/favicon.ico?ver=1">
         {{imp[1][5]|safe}}
         {{imp[2][3]|safe}}
         <meta name="title" content="{{imp[0]}} - {{imp[1][0]}}">
@@ -45,7 +45,7 @@
                                 </a>
                             </div>
                         </div>
-                        |
+                        
                         <div id="top_tool_cel">
                             <a href="javascript:void(0);" onclick="opening('other_cel');">
                                 <i class="fas fa-plus-circle"></i>
@@ -69,20 +69,22 @@
                                 </a>
                             </div>
                         </div>
-                        |
+                        
                         <div id="top_tool_cel">
                             <a href="/user">
                                 {% if imp[2][2] == 1 %}
-                                    <i class="fas fa-user"></i>
-                                {% elif imp[2][2] == 0 %}
-                                    <i class="fas fa-user-times"></i>
+                                    {% if imp[2][8] != 0 %}
+                                        <i class="fas fa-user-secret"></i>
+                                    {% else %}
+                                        <i class="fas fa-user"></i>
+                                    {% endif %}
                                 {% else %}
-                                    <i class="fas fa-user-secret"></i>
+                                    <i class="fas fa-user-times"></i>
                                 {% endif %}
                                 {{imp[2][5]}}
                             </a>
                         </div>
-                        |
+                        
                         <form method="post" action="/search" id="search" role="search">
                             <input id="search_input" name="search" placeholder="{{'search'|load_lang}}" autocomplete="off" type="search">
                             |
@@ -106,12 +108,11 @@
                         <button id="mobile_button_first" type="submit" formaction="/goto"><i class="fas fa-sign-in-alt"></i></button>
                         |
                         <button type="submit" formaction="/search"><i class="fas fa-search"></i></button>
-                    </form>
-                    <div id="close_top">
+                        |
                         <a href="javascript:void(0);" onclick="opening('mobile_menu');">
                             <i class="fas fa-bars"></i>
                         </a>
-                    </div>
+                    </form>
                     <h2>{{'recent'|load_lang}}</h2>
                     <li><a href="/recent_changes">{{'edit'|load_lang}}</a></li>
                     <li><a href="/recent_discuss">{{'discussion'|load_lang}}</a></li>
@@ -129,7 +130,11 @@
                         <div id="tool" class="not_mobile">
                             {% for sub_d in menu %}
                                 <div id="tool_cel">
-                                    <a class="menu-item" href="/{{sub_d[0]}}">{{sub_d[1]}}</a>
+                                    {% if sub_d|length > 2 and sub_d[2] == 1 %}
+                                        <a class="menu-item" href="/{{sub_d[0]}}" id="topic_color">{{sub_d[1]}}</a>
+                                    {% else %}
+                                        <a class="menu-item" href="/{{sub_d[0]}}">{{sub_d[1]}}</a>
+                                    {% endif %}
                                 </div>
                                 {% if menu[loop.index] %}
                                     |
@@ -137,17 +142,26 @@
                             {% endfor %}
                         </div>
                     {% endif %}
-                    <h1>
+                    <h1 id="title">
                         {{imp[0]}}
                         {% if imp[3][0] != 0 %}
                             <sub>{{imp[3][0]}}</sub>
                         {% endif %}
                     </h1>
+                    <div id="last_edit">
+                        {% if imp[3][1] != 0 %}
+                            {{'last_edit_time'|load_lang}} : {{imp[3][1]}}
+                        {% endif %}
+                    </div>
                     {% if menu != 0 %}
                         <div id="tool" class="is_mobile">
                             {% for sub_d in menu %}
                                 <div id="tool_cel">
-                                    <a class="menu-item" href="/{{sub_d[0]}}">{{sub_d[1]}}</a>
+                                    {% if sub_d|length > 2 and sub_d[2] == 1 %}
+                                        <a class="menu-item" href="/{{sub_d[0]}}" id="topic_color">{{sub_d[1]}}</a>
+                                    {% else %}
+                                        <a class="menu-item" href="/{{sub_d[0]}}">{{sub_d[1]}}</a>
+                                    {% endif %}
                                 </div>
                                 {% if menu[loop.index] %}
                                     |

+ 1 - 1
views/neo_yousoro/info.json

@@ -1,5 +1,5 @@
 {
     "name" : "Neo_Yousoro",
-    "skin_ver" : "v1.0.2",
+    "skin_ver" : "v1.0.6",
     "require_ver" : "2"
 }