Bladeren bron

Merge pull request #611 from 2du/master

New Stable
잉여강화기 (SPUP) 7 jaren geleden
bovenliggende
commit
d86efe925d
90 gewijzigde bestanden met toevoegingen van 5675 en 3469 verwijderingen
  1. 1 1
      .github/PULL_REQUEST_TEMPLATE.md
  2. BIN
      .github/logo.png
  3. 3 3
      .gitignore
  4. 10 11
      Docker-Install.md
  5. 12 8
      Dockerfile
  6. 19 0
      Dockerfile.arm32v7
  7. 19 0
      Dockerfile.arm64v8
  8. 154 2887
      app.py
  9. 6 0
      data/app_variables.json
  10. 20 0
      data/oauthsettings.json
  11. 63 29
      emergency_tool.py
  12. 245 131
      language/en-US.json
  13. 244 128
      language/ko-KR.json
  14. 0 18
      language/test.py
  15. 36 37
      readme-ko.md
  16. 49 21
      readme.md
  17. 117 0
      route/acl.py
  18. 47 0
      route/acl_list.py
  19. 23 0
      route/admin_list.py
  20. 26 0
      route/admin_log.py
  21. 85 0
      route/admin_plus.py
  22. 65 0
      route/adsense_setting.py
  23. 25 0
      route/alarm.py
  24. 91 0
      route/block_log.py
  25. 120 0
      route/change_password.py
  26. 83 0
      route/check_key.py
  27. 69 0
      route/close_topic_list.py
  28. 34 0
      route/count_edit.py
  29. 51 0
      route/custom_head_view.py
  30. 61 0
      route/deep_search.py
  31. 9 0
      route/del_alarm.py
  32. 20 0
      route/del_inter.py
  33. 57 0
      route/delete.py
  34. 30 0
      route/diff_data.py
  35. 18 0
      route/down.py
  36. 118 0
      route/edit.py
  37. 22 0
      route/give_log.py
  38. 17 0
      route/history_hidden.py
  39. 11 0
      route/image_view.py
  40. 60 0
      route/indexing.py
  41. 75 0
      route/inter_wiki.py
  42. 91 0
      route/login.py
  43. 187 0
      route/login_oauth.py
  44. 83 0
      route/manager.py
  45. 103 0
      route/move.py
  46. 67 0
      route/need_email.py
  47. 19 0
      route/not_close_topic.py
  48. 57 0
      route/now_update.py
  49. 100 0
      route/oauth_setting.py
  50. 45 0
      route/other.py
  51. 29 0
      route/please.py
  52. 92 0
      route/plus_inter.py
  53. 41 0
      route/preview.py
  54. 47 0
      route/raw_view.py
  55. 172 0
      route/read_view.py
  56. 178 0
      route/recent_changes.py
  57. 44 0
      route/recent_discuss.py
  58. 109 0
      route/register.py
  59. 24 0
      route/restart.py
  60. 77 0
      route/revert.py
  61. 403 0
      route/setting.py
  62. 72 0
      route/title_index.py
  63. 177 99
      route/tool/func.py
  64. 68 0
      route/tool/init.py
  65. 5 5
      route/tool/mark.py
  66. 0 0
      route/tool/set_mark/namu.py
  67. 0 0
      route/tool/set_mark/tool.py
  68. 201 0
      route/topic.py
  69. 77 0
      route/topic_admin.py
  70. 55 0
      route/topic_stop.py
  71. 23 0
      route/topic_top.py
  72. 92 0
      route/upload.py
  73. 66 0
      route/user_admin.py
  74. 82 0
      route/user_ban.py
  75. 92 0
      route/user_check.py
  76. 101 0
      route/user_info.py
  77. 57 0
      route/user_log.py
  78. 32 0
      route/user_tool.py
  79. 55 0
      route/user_topic_list.py
  80. 25 0
      route/watch_list.py
  81. 24 0
      route/watch_list_name.py
  82. 35 0
      route/xref.py
  83. 0 6
      views/easter_egg.html
  84. 2 1
      views/main_css/main.css
  85. 56 0
      views/main_css/oauth.css
  86. 1 1
      views/main_css/topic_reload.js
  87. 14 7
      views/neo_yousoro/css/main.css
  88. 11 6
      views/neo_yousoro/index.html
  89. 0 70
      views/neo_yousoro/js/main.js
  90. 69 0
      views/neo_yousoro/js/skin_set.js

+ 1 - 1
.github/PULL_REQUEST_TEMPLATE.md

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

BIN
.github/logo.png


+ 3 - 3
.gitignore

@@ -1,12 +1,12 @@
 set.json
 set.json
 
 
-set_mark/__pycache__
-/__pycache__
+__pycache__
 /app_session
 /app_session
 .vscode
 .vscode
 
 
 *.db
 *.db
-image
+*.db-journal
+images
 robots.txt
 robots.txt
 
 
 views/liberty
 views/liberty

+ 10 - 11
Docker-Install.md

@@ -1,11 +1,10 @@
-## Installation
-```
-docker pull hotococoa/opennamu
-```
-
-## Setting
-```
-export NAMU_PORT=port
-export NAMU_LANG=lang
-```
-Default: Port 3000, en-US
+## Installation
+```
+docker pull opennamu/stable
+```
+
+## Start
+```
+docker run -p 3000:3000 -v data:/app/data --name opennamu opennamu/stable
+docker run -p <host-port>:3000 -v <host-data_directory>:/app/data --name <docker-containername> opennamu/stable
+```

+ 12 - 8
Dockerfile

@@ -1,15 +1,19 @@
-FROM ubuntu:16.04
+FROM python:3.6.8-stretch
 
 
-MAINTAINER Hoto Cocoa <cocoa@hoto.us>
+MAINTAINER 2du <min08101@naver.com>
+MAINTAINER hoparkgo9ma <me@ho9.me>
 
 
-ENV NAMU_PORT=3000
-ENV NAMU_LANG=en-US
+ENV NAMU_DB = data
+ENV NAMU_HOST = 0.0.0.0
+ENV NAMU_PORT = 3000
+ENV NAMU_LANG = en-US
+ENV NAMU_MARKUP = namumark
+ENV NAMU_ENCRYPT = sha3
 
 
 ADD . /app
 ADD . /app
-
 WORKDIR /app
 WORKDIR /app
 
 
-RUN apt update && apt install -y --no-install-recommends python3 python3-dev python3-pip python3-setuptools
-RUN python3 -m pip install pip --upgrade && python3 -m pip install -r requirements.txt
+RUN pip install -r requirements.txt
+EXPOSE 3000
 
 
-CMD python3 app.py
+CMD [ "python", "./app.py" ]

+ 19 - 0
Dockerfile.arm32v7

@@ -0,0 +1,19 @@
+FROM arm32v7/python:3.6.8-stretch
+
+MAINTAINER 2du <min08101@naver.com>
+MAINTAINER hoparkgo9ma <me@ho9.me>
+
+ENV NAMU_DB = data
+ENV NAMU_HOST = 0.0.0.0
+ENV NAMU_PORT = 3000
+ENV NAMU_LANG = en-US
+ENV NAMU_MARKUP = namumark
+ENV NAMU_ENCRYPT = sha3
+
+ADD . /app
+WORKDIR /app
+
+RUN pip install -r requirements.txt
+EXPOSE 3000
+
+CMD [ "python", "./app.py" ]

+ 19 - 0
Dockerfile.arm64v8

@@ -0,0 +1,19 @@
+FROM arm64v8/python:3.6.8-stretch
+
+MAINTAINER 2du <min08101@naver.com>
+MAINTAINER hoparkgo9ma <me@ho9.me>
+
+ENV NAMU_DB = data
+ENV NAMU_HOST = 0.0.0.0
+ENV NAMU_PORT = 3000
+ENV NAMU_LANG = en-US
+ENV NAMU_MARKUP = namumark
+ENV NAMU_ENCRYPT = sha3
+
+ADD . /app
+WORKDIR /app
+
+RUN pip install -r requirements.txt
+EXPOSE 3000
+
+CMD [ "python", "./app.py" ]

File diff suppressed because it is too large
+ 154 - 2887
app.py


+ 6 - 0
data/app_variables.json

@@ -0,0 +1,6 @@
+{
+    "_README" : "DO NOT MODIFY THIS FILE.",
+    "path_oauth_setting" : "data/oauthsettings.json",
+    "path_set_json" : "data/set.json",
+    "path_data_image" : "data/images"
+}

+ 20 - 0
data/oauthsettings.json

@@ -0,0 +1,20 @@
+{
+    "_README" : {
+        "en" : "To use the oAuth login feature, you must set the 'publish_url' value to a domain address that includes the HTTPS protocol, and actually support HTTPS connections.",
+        "ko" : "oAuth 로그인 기능을 사용하려면 'publish_url' 값을 HTTPS 프로토콜을 포함한 도메인 주소로 설정하고, 실제로 HTTPS 연결을 지원해야 합니다.",
+        "support" : ["discord", "facebook", "naver"]
+    },
+    "publish_url" : "https://",
+    "discord" : {
+        "client_id" : "",
+        "client_secret" : ""
+    },
+    "facebook" : {
+        "client_id" : "",
+        "client_secret" : ""
+    },
+    "naver" : {
+        "client_id" : "",
+        "client_secret" : ""
+    }
+}

+ 63 - 29
emergency_tool.py

@@ -1,29 +1,50 @@
+import os
 import json
 import json
 import sqlite3
 import sqlite3
-import bcrypt
 import hashlib
 import hashlib
 import threading
 import threading
 
 
-from func import *
-from mark import load_conn2, namumark
+from route.tool.func import *
+from route.tool.mark import load_conn2, namumark
 
 
-json_data = open('set.json').read()
-set_data = json.loads(json_data)
+all_src = []
+for i_data in os.listdir("."):
+    f_src = re.search("(.+)\.db$", i_data)
+    if f_src:
+        all_src += [f_src.groups()[0]]
 
 
-conn = sqlite3.connect(set_data['db'] + '.db', check_same_thread = False)
+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)
+
+    print('Number : ', end = '')    
+    db_name = all_src[int(number_check(input())) - 1]
+else:
+    db_name = all_src[0]
+
+if len(all_src) == 1:
+    print('DB\'s name : ' + db_name)
+
+conn = sqlite3.connect(db_name + '.db', check_same_thread = False)
 curs = conn.cursor()
 curs = conn.cursor()
 
 
 load_conn(conn)
 load_conn(conn)
 
 
-print('1. backlink reset')
-print('2. recaptcha delete')
-print('3. ban delete')
-print('4. change port')
-print('5. change skin')
-print('6. change password')
-print('7. reset version')
-
-print('select : ', end = '')
+print('1. Backlink reset')
+print('2. reCAPTCHA delete')
+print('3. Ban delete')
+print('4. Change host')
+print('5. Change port')
+print('6. Change skin')
+print('7. Change password')
+print('8. Reset version')
+print('9. New DB create')
+
+print('Select : ', end = '')
 what_i_do = input()
 what_i_do = input()
 
 
 if what_i_do == '1':
 if what_i_do == '1':
@@ -50,7 +71,7 @@ elif what_i_do == '2':
     curs.execute("delete from other where name = 'recaptcha'")
     curs.execute("delete from other where name = 'recaptcha'")
     curs.execute("delete from other where name = 'sec_re'")
     curs.execute("delete from other where name = 'sec_re'")
 elif what_i_do == '3':
 elif what_i_do == '3':
-    print('ip or name : ', end = '')
+    print('IP or Name : ', end = '')
     user_data = input()
     user_data = input()
 
 
     if re.search("^([0-9]{1,3}\.[0-9]{1,3})$", user_data):
     if re.search("^([0-9]{1,3}\.[0-9]{1,3})$", user_data):
@@ -61,36 +82,49 @@ elif what_i_do == '3':
         curs.execute("insert into rb (block, end, today, blocker, why, band) values (?, ?, ?, ?, ?, ?)", [user_data, load_lang('release', 1), get_time(), load_lang('tool', 1) + ':emergency', '', band])
         curs.execute("insert into rb (block, end, today, blocker, why, band) values (?, ?, ?, ?, ?, ?)", [user_data, load_lang('release', 1), get_time(), load_lang('tool', 1) + ':emergency', '', band])
     curs.execute("delete from ban where block = ?", [user_data])
     curs.execute("delete from ban where block = ?", [user_data])
 elif what_i_do == '4':
 elif what_i_do == '4':
-    print('port : ', end = '')
-    port = input()
+    print('Host : ', end = '')
+    host = input()
 
 
-    curs.execute("update other set data = ? where name = 'port'", [port])
+    curs.execute("update other set data = ? where name = 'host'", [host])
 elif what_i_do == '5':
 elif what_i_do == '5':
-    print('skin name : ', end = '')
+    print('Port : ', end = '')
+    port = int(input())
+
+    curs.execute("update other set data = ? where name = 'port'", [port])
+elif what_i_do == '6':
+    print('Skin\'s name : ', end = '')
     skin = input()
     skin = input()
 
 
     curs.execute("update other set data = ? where name = 'skin'", [skin])
     curs.execute("update other set data = ? where name = 'skin'", [skin])
-elif what_i_do == '6':
+elif what_i_do == '7':
     print('1. sha256')
     print('1. sha256')
-    print('2. bcrypt')
-    print('select : ', end = '')
-    what_i_do = input()
+    print('2. sha3')
+    print('Select : ', end = '')
+    what_i_do = int(input())
 
 
-    print('user name : ', end = '')
+    print('User\'s name : ', end = '')
     user_name = input()
     user_name = input()
 
 
-    print('user password : ', end = '')
+    print('User\'s password : ', end = '')
     user_pw = input()
     user_pw = input()
 
 
     if what_i_do == '1':
     if what_i_do == '1':
         hashed = hashlib.sha256(bytes(user_pw, 'utf-8')).hexdigest()
         hashed = hashlib.sha256(bytes(user_pw, 'utf-8')).hexdigest()
     elif what_i_do == '2':
     elif what_i_do == '2':
-        hashed = bcrypt.hashpw(bytes(user_pw, 'utf-8'), bcrypt.gensalt()).decode()
+        hashed = sha3_256(bytes(user_pw, 'utf-8')).hexdigest()
        
        
     curs.execute("update user set pw = ? where id = ?", [hashed, user_name])
     curs.execute("update user set pw = ? where id = ?", [hashed, user_name])
-elif what_i_do == '7':
+elif what_i_do == '8':
     curs.execute("update other set data = '00000' where name = 'ver'")
     curs.execute("update other set data = '00000' where name = 'ver'")
+elif what_i_do == '9':
+    print('DB\'s name (data) : ', end = '')
+    
+    db_name = input()
+    if db_name == '':
+        db_name = 'data'
+
+    sqlite3.connect(db_name + '.db', check_same_thread = False)
 
 
 conn.commit()
 conn.commit()
 
 
-print('ok')
+print('OK')

+ 245 - 131
language/en-US.json

@@ -1,132 +1,246 @@
 {
 {
-    "edit" : "edit",
-    "raw" : "raw",
-    "history" : "history",
-    "delete" : "delete",
-    "logo" : "logo",
-    "regex" : "regex",
-    "frontpage" : "frontpage",
-    "max_file_size" : "max file size",
-    "backup_interval" : "backup interval",
-    "default" : "default",
-    "language" : "language",
-    "port" : "port",
-    "secret_key" : "secret key",
-    "update_branch" : "update branch",
-    "main" : "main",
-    "indexing" : "indexing",
-    "easy" : "easy",
-    "server" : "server",
-    "load" : "load",
-    "skin" : "skin",
-    "view" : "view",
-    "bottom" : "bottom",
-    "text" : "notice",
-    "template" : "template",
-    "move" : "move",
-    "hide" : "hide",
-    "list" : "list",
-    "id" : "id",
-    "out" : "out",
-    "revert" : "undo",
-    "version" : " ver",
-    "normal_version" : "version",
-    "new" : "new",
-    "document" : "documents",
-    "all" : "everything",
-    "ban" : "ban",
-    "release" : "release",
-    "save" : "save",
-    "other" : "other",
-    "tool" : "tools",
-    "plus" : "add",
-    "open" : "open",
-    "search" : "search",
-    "need" : "needful",
-    "upload" : "file upload",
-    "record" : "record",
-    "name" : "name",
-    "license" : "license",
-    "interwiki" : "interwiki",
-    "update" : "update",
-    "setting" : "set",
-    "create" : "create",
-    "editor" : "editor",
-    "hour" : "hour",
-    "time" : "time",
-    "close" : "close",
-    "stop" : "stop",
-    "restart" : "restart",
-    "agreement" : "agreement",
-    "backlink" : "backlink",
-    "why" : "reason",
-    "random" : "random",
-    "authority" : "authority",
-    "file" : "file",
-    "change" : "changes",
-    "compare" : "compare",
-    "count" : "count",
-    "check" : "check",
-    "user" : "user",
-    "alarm" : "alarm",
-    "preview" : "preview",
-    "watchlist" : "watching list",
-    "state" : "state",
-    "recent" : "recent",
-    "discussion" : "debate",
-    "login" : "login",
-    "logout" : "logout",
-    "register" : "register",
-    "able" : "able",
-    "second": "second",
-    "normal" : "normal",
-    "subscriber" : "registor",
-    "admin" : "admin",
-    "next" : "next",
-    "previous" : "previous",
-    "owner" : "owner",
-    "admin_group" : "mod group",
-    "limitless" : "limitless",
-    "period" : "period",
-    "now" : "now",
-    "blocked" : "blocked",
-    "band" : "band",
-    "notice" : "notice",
-    "writer" : "writer",
-    "upper" : "upper",
-    "under" : "under",
-    "pass" : "pass",
-    "category" : "category",
-    "filter" : "filter",
-    "send" : "send",
-    "reload" : "reload",
-    "password" : "password",
-    "confirm" : "confirm",
-
-    "user_head_warring" : "user's head will deleted if you close the browser or when you are editting as guest",
-    "http_warring" : "warning : if you are not on https connection, your information can be leaked. we won't response to that.",
-    "no_login_warring" : "non-login status. ip is logged when working with non-login.",
-    
-    "authority_error" : "insufficient privileges.",
-    "no_login_error" : "non-login status.",
-    "no_exist_user_error" : "account does not exist.",
-    "no_admin_block_error" : "administrators can not block, check.",
-    "skin_error" : "this skin is not support setting.",
-    "same_id_exist_error" : "there are users with the same id.",
-    "long_id_error" : "id must be shorter than 20 characters.",
-    "id_char_error" : "only hangul, alphabet and space are allowed for id.",
-    "file_exist_error" : "file does not exist.",
-    "password_error" : "the password is different.",
-    "recaptcha_error" : "go through the recaptcha.",
-    "file_extension_error" : "only jpg, gif, jpeg, png, webp is possible.",
-    "edit_record_error" : "edit record can not be more than 500 characters.",
-    "same_file_error" : "a file with the same name exists.",
-    "file_capacity_error" : "maximum file capacity (mb) :",
-    "decument_exist_error" : "the document already exists where you want to move it.",
-    "password_diffrent_error" : "reconfirm password and input password are different.",
-    "edit_filter_error" : "censored by edit filter.",
-    "file_name_error" : "only alphabet, hangul, space, underscore, and minus signs are allowed for file names.",
-
-    "register_text" : "register text",
-    "non_login_alert" : "non-login alert"
-}
+    "_comment_1_" : "Common",
+        "server" : "Server",
+        "filter" : "Filter",
+        "delete" : "Delete",
+        "notice" : "Notice",
+        "add" : "Add",
+        "etc" : "Etc",
+        "name" : "Name",
+        "regex" : "Regex",
+        "id" : "ID",
+        "list" : "List",
+        "main" : "Main",
+        "return" : "Return",
+        "skin" : "Skin",
+        "save" : "Save",
+        "secret_key" : "Secret key",
+        "host" : "Host",
+        "port" : "Port",
+        "restart" : "Restart",
+        "document_name" : "Document[s] name",
+        "discussion_name" : "Discussion[s] name",
+        "user_name" : "User[s] name",
+        "go" : "Go",
+        "document" : "Document",
+        "discussion" : "Discussion",
+        "backlink" : "Backlink",
+        "closed" : "Closed",
+        "reload" : "Reload",
+        "send" : "Send",
+        "ongoing" : "Ongoing",
+        "normal" : "Normal",
+        "range" : "Range",
+        "search" : "Search",
+        "raw" : "Raw",
+        "history" : "History",
+        "user_discussion" : "User discussion",
+        "record" : "Record",
+        "state" : "State",
+        "revert" : "Revert",
+        "why" : "Reason",
+        "edit" : "Edit",
+        "preview" : "Preview",
+        "move" : "Move",
+        "upload" : "Upload",
+        "version" : "Version",
+        "stop" : "Stop",
+        "close" : "Closed",
+        "open" : "Open",
+        "agreement" : "Agreement",
+        "template" : "Template",
+        "category" : "Category",
+        "file" : "File",
+        "writer" : "Writer",
+        "editor" : "Editor",
+        "hide" : "Hide",
+        "check" : "Check",
+        "destruction" : "Destruction",
+        "tool" : "Tool",
+        "recent" : "Recently",
+        "password" : "Password",
+        "login" : "Sign-in",
+        "logout" : "Sing-out",
+        "register" : "Sign-up",
+        "language" : "Language",
+        "compare" : "Compare",
+        "email" : "Email",
+        "key" : "Key",
+        "all" : "All",
+        "sub" : "Sub",
+        "create" : "Create",
+        "acl" : "ACL",
+        "upper" : "Parent",
+        "other" : "Other",
+        "random" : "Random",
+        "error" : "Error",
+        "next" : "Next",
+        "previous" : "Previous",
+        "authority" : "Authority",
+        "connect" : "Connect",
+        "_comment_1.1_" : "Time",
+            "second" : "Second(s)",
+            "hour" : "Hour(s)",
+            "limitless" : "No time limit",
+            "time" : "Time",
+            "period" : "Period",
+            "end" : "End",
+        "_comment_1.2_" : "User",
+            "user" : "User",
+            "admin" : "Admin",
+            "owner" : "Owner",
+            "ip" : "IP",
+            "member" : "Member",
+        "_comment_1.3_" : "Ban",
+            "ban" : "Block",
+            "blocked" : "Blocked",
+            "release" : "Release",
+    "_comment_2_" : "Func",
+        "wiki_restart" : "Wiki engine restart",
+        "update" : "Update",
+        "need_document" : "Required Document(s)",
+        "close_discussion" : "Closed discussion(s)",
+        "open_discussion" : "Open discussion(s)",
+        "recent_discussion" : "Recently discussion(s)",
+        "recent_change" : "Recently edit(s)",
+        "edit_filter" : "Contents filter",
+        "recent_ban" : "Recently Block(s)",
+        "load" : "Import another document",
+        "edit_filter_rule" : "Contents filter[s] rule",
+        "move_history" : "History of move",
+        "other_tool" : "Other tool",
+        "admin_tool" : "Admin[s] tool",
+        "check_user" : "Check user",
+        "compare_target" : "Comparison target name",
+        "authorize" : "Authorize",
+        "indexing" : "DB Indexing",
+        "hide_release" : "Unhide",
+        "notice_release" : "Release notice",
+        "ban_release" : "Unblock",
+        "discussion_tool" : "Discussion tools",
+        "discussion_raw" : "Discussion[s] raw",
+        "oauth_signin_facebook" : "Sign-in with Facebook",
+        "oauth_signin_naver" : "Sign-in with NAVER",
+        "connection" : "Connection",
+        "new_connection" : "Connect...",
+        "user_setting" : "User settings",
+        "now_password" : "Now password",
+        "new_password" : "New password",
+        "password_confirm" : "Password confirm",
+        "oauth_connection" : "Oauth Connection",
+        "password_search" : "Password finder",
+        "login_able" : "Loginable",
+        "band_ban" : "Range block",
+        "band_blocked" : "Range blocked",
+        "document_acl" : "Document[s] ACL",
+        "discussion_acl" : "Discussion[s] ACL",
+        "view_acl" : "Document viewed ACL",
+        "under_category" : "Sub-category",
+        "count" : "Number of Contributions",
+        "alarm" : "Notice(s)",
+        "user_document" : "User[s] document",
+        "user_head" : "User[s] <head>",
+        "user_document_acl" : "User[s] document ACL",
+        "_comment_2.1_" : "Filter",
+            "_comment_2.1.1_" : "List",
+                "interwiki_list" : "Interwiki(s) list",
+                "email_filter_list" : "Email filter(s) list",
+                "id_filter_list" : "ID filter(s) list",
+                "edit_filter_list" : "Contents filter(s) list",
+            "_comment_2.1.2_" : "Add",
+                "interwiki_add" : "Interwiki add",
+                "edit_filter_add" : "Contents filter add",
+                "id_filter_add" : "ID filter add",
+                "email_filter_add" : "Email filter add",
+        "_comment_2.2_" : "Setting",
+            "setting" : "Setting",
+            "restart_required" : "Restart required",
+            "oauth_setting" : "OAuth settings",
+            "adsense_setting" : "Adsense settings",
+            "adsense_enable" : "Adsense enable",
+            "skin_setting" : "Skin settings",
+            "_comment_2.2.1_" : "List",
+                "main_setting" : "Main settings",
+                "text_setting" : "Text settings",
+                "main_head" : "Global <head>",
+                "main_body" : "Global <body>",
+            "_comment_2.2.2_" : "Main",
+                "wiki_name" : "Wiki[s] name",
+                "wiki_logo" : "Wiki[s] logo",
+                "main_page" : "Main page",
+                "bottom_text" : "Bottom text",
+                "max_file_size" : "Max file size",
+                "backup_interval" : "Backup Cycles",
+                "wiki_skin" : "Wiki[s] Skin",
+                "default_acl" : "Default edit ACL",
+                "default_discussion_acl" : "Default discussion ACL",
+                "no_register" : "No sign-up",
+                "hide_ip" : "Hide IP",
+                "wiki_host" : "Wiki[s] host",
+                "wiki_port" : "Wiki[s] port",
+                "wiki_secret_key" : "Wiki[s] secret key",
+                "email_required" : "Email required",
+                "google_imap_required" : "Google IMAP setting required",
+                "update_branch" : "Branch to import updates",
+            "_comment_2.2.3_" : "Text",
+                "register_text" : "Terms of sign-up",
+                "non_login_alert" : "Non-login alert",
+            "_comment_2.2.4_" : "Google",
+                "recaptcha" : "reCAPTCHA",
+                "google_imap" : "Google IMAP",
+                "google_email" : "Google email",
+                "google_app_password" : "Google APP password",
+        "_comment_2.3_" : "List",
+            "open_discussion_list" : "Open discussion(s) list",
+            "discussion_list" : "Discussion(s) list",
+            "admin_list" : "Admin(s) list",
+            "member_list" : "Member(s) list",
+            "authority_use_list" : "Authority use list",
+            "admin_group_list" : "Admin group(s) list",
+            "all_document_list" : "All document(s) list",
+            "watchlist" : "Watchlist",
+            "_comment_2.3.1_" : "ACL document list",
+                "acl_document_list" : "ACL document(s) list",
+                "acl_required" : "Required ACL",
+            "_comment_2.3.2_" : "ACL List",
+                "admin_group_add" : "Admin group add",
+                "ban_authority" : "Block authority",
+                "discussion_authority" : "Discussion manage authority",
+                "user_check_authority" : "User check authority",
+                "document_acl_authority" : "Document ACL manage authority",
+                "history_hide_authority" : "History hide authority",
+                "authorization_authority" : "Authorization authority",
+                "owner_authority" : "Owner authority",
+            "_comment_2.3.3_" : "Record",
+                "edit_record" : "Edit record",
+                "discussion_record" : "Discussion record",
+    "_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",
+        "no_login_warring" : "Non-login status. IP is logged when working with non-login.",
+        "_comment_3.1_" : "Error",
+            "update_error" : "Auto update is not support.",
+            "inter_error" : "Internal error.",
+            "authority_error" : "Insufficient privileges.",
+            "no_login_error" : "Non-login status.",
+            "no_exist_user_error" : "Account does not exist.",
+            "no_admin_block_error" : "Administrators can not block, check.",
+            "skin_error" : "This skin is not support setting.",
+            "same_id_exist_error" : "There are users with the same ID.",
+            "long_id_error" : "ID must be shorter than 20 characters.",
+            "id_char_error" : "Only hangul, alphabet and space are allowed for ID.",
+            "file_exist_error" : "File does not exist.",
+            "password_error" : "The password is different.",
+            "recaptcha_error" : "Go through the recaptcha.",
+            "file_extension_error" : "Only jpg, gif, jpeg, png, webp is possible.",
+            "edit_record_error" : "Edit reason can not be more than 500 characters.",
+            "same_file_error" : "A file with the same name exists.",
+            "file_capacity_error" : "Maximum file capacity (MB) :",
+            "decument_exist_error" : "The document already exists where you want to move it.",
+            "password_diffrent_error" : "Reconfirm password and input password are different.",
+            "edit_filter_error" : "Censored by edit filter.",
+            "file_name_error" : "Only alphabet, hangul, space, underscore, and minus signs are allowed for file names."
+}

+ 244 - 128
language/ko-KR.json

@@ -1,130 +1,246 @@
 {
 {
-    "edit": "편집",
-    "raw" : "원본",
-    "history": "역사",
-    "easy": "간단",
-    "skin": "스킨",
-    "delete": "삭제",
-    "regex" : "정규표현식",
-    "language" : "언어",
-    "server": "서버",
-    "filter": "필터",
-    "move": "이동",
-    "hide": "숨김",
-    "list": "목록",
-    "id" : "아이디",
-    "revert": "되돌리기",
-    "version": "판",
-    "normal_version": "버전",
-    "document": "문서",
-    "all": "모든",
-    "ban": "차단",
-    "release": "해제",
-    "save": "저장",
-    "other": "기타",
-    "tool": "도구",
-    "plus": "추가",
-    "open": "열기",
-    "search": "검색",
-    "user": "사용자",
-    "alarm": "알림",
-    "watchlist": "주시 문서",
-    "recent": "최근",
-    "recent_changes": "최근 변경",
-    "discussion": "토론",
-    "login": "로그인",
-    "logout": "로그아웃",
-    "register": "회원가입",
-    "able": "가능",
-    "second": "초",
-    "normal": "일반",
-    "subscriber": "가입자",
-    "admin": "관리자",
-    "owner": "소유자",
-    "admin_group": "관리 그룹",
-    "user_head_warring": "비 로그인의 경우에는 사용자 head가 로그인하거나 브라우저 닫으면 날아갑니다.",
-    "http_warring": "주의 : 만약 https 연결이 아닌 경우 데이터가 유출될 가능성이 있습니다. 이에 대해 책임지지 않습니다.",
-    "new": "새",
-    "need": "필요한",
-    "upload": "파일 올리기",
-    "record": "기록",
-    "name": "이름",
-    "license": "라이선스",
-    "bottom" : "하단",
-    "text" : "문구",
-    "interwiki": "인터위키",
-    "update": "업데이트",
-    "setting": "설정",
-    "create": "생성",
-    "editor": "수정자",
-    "hour": "시간",
-    "time": "시각",
-    "close": "닫기",
-    "stop": "정지",
-    "restart": "재시작",
-    "agreement": "합의",
-    "load" : "불러오기",
-    "backlink": "역링크",
-    "why": "사유",
-    "random": "무작위",
-    "authority": "권한",
-    "file": "파일",
-    "change": "변경",
-    "compare": "비교",
-    "count": "횟수",
-    "check": "검사",
-    "view" : "보기",
-    "preview": "미리보기",
-    "next": "다음",
-    "previous": "이전",
-    "no_login_warring": "비 로그인 상태로 진행 시 ip가 기록될 수 있습니다.",
-    "state": "상태",
-    "limitless": "무기한",
-    "period": "기간",
-    "now": "현재",
-    "blocked": "차단자",
-    "band": "대역",
-    "notice": "공지",
-    "writer": "작성자",
-    "upper": "상위",
-    "under": "하위",
-    "pass": "통과",
-    "category": "분류",
-    "send" : "전송",
-    "reload" : "새로고침",
-    "password" : "암호",
-    "confirm" : "확인",
-    "authority_error": "권한이  부족합니다.",
-    "no_login_error": "비 로그인 상태 입니다.",
-    "no_exist_user_error": "계정이 없습니다.",
-    "no_admin_block_error": "관리자는 차단, 검사 할 수 없습니다.",
-    "skin_error" : "이 스킨은 스킨 설정을 지원하지 않습니다.",
-    "same_id_exist_error": "동일한 아이디의 사용자가 있습니다.",
-    "long_id_error": "아이디는 20글자보다 짧아야 합니다.",
-    "id_char_error": "아이디에는 한글과 알파벳과 공백만 허용 됩니다.",
-    "file_exist_error": "파일이 없습니다.",
-    "password_error": "비밀번호가 다릅니다.",
-    "recaptcha_error": "리캡차를 통과하세요.",
-    "file_extension_error": "jpg, gif, jpeg, png, webp만 가능 합니다.",
-    "edit_record_error": "편집 기록은 500자를 넘을 수 없습니다.",
-    "same_file_error": "동일한 이름의 파일이 있습니다.",
-    "file_capacity_error": "파일 최대 용량 (mb) :",
-    "decument_exist_error": "내용이 원래 문서와 동일 합니다.",
-    "password_diffrent_error": "재 확인 비밀번호와 입력 비밀번호가 다릅니다.",
-    "edit_filter_error": "편집 필터에 의해 검열 되었습니다.",
-    "file_name_error": "파일 이름은 알파벳, 한글, 띄어쓰기, 언더바,  빼기표만 허용 됩니다.",
-    "template": "틀",
-    "out": "외부",
-    "logo": "로고",
-    "frontpage": "대문",
-    "max_file_size": "최대 파일 크기",
-    "backup_interval": "백업 간격",
-    "default": "기본",
-    "port": "포트",
-    "secret_key": "비밀키",
-    "update_branch": "업데이트 브랜치",
-    "main": "메인",
-    "indexing" : "인덱싱",
-    "register_text" : "회원가입 문구",
-    "non_login_alert" : "비 로그인 경고문"
+    "_comment_1_" : "공통",
+        "server" : "서버",
+        "filter" : "필터",
+        "delete" : "삭제",
+        "notice" : "알림",
+        "add" : "추가",
+        "etc" : "기타",
+        "name" : "이름",
+        "regex" : "정규표현식",
+        "id" : "아이디",
+        "list" : "목록",
+        "main" : "메인",
+        "return" : "돌아가기",
+        "skin" : "스킨",
+        "save" : "저장",
+        "secret_key" : "비밀키",
+        "host" : "호스트",
+        "port" : "포트",
+        "restart" : "재시작",
+        "document_name" : "문서명",
+        "discussion_name" : "토론명",
+        "user_name" : "사용자 이름",
+        "go" : "이동",
+        "document" : "문서",
+        "discussion" : "토론",
+        "backlink" : "역링크",
+        "closed" : "닫힘",
+        "reload" : "다시 로드",
+        "send" : "보냄",
+        "ongoing" : "진행중",
+        "normal" : "보통",
+        "range" : "범위",
+        "search" : "검색",
+        "raw" : "원본",
+        "history" : "역사",
+        "user_discussion" : "사용자 토론",
+        "record" : "기록",
+        "state" : "상태",
+        "revert" : "복원",
+        "why" : "사유",
+        "edit" : "편집",
+        "preview" : "미리보기",
+        "move" : "이동",
+        "upload" : "업로드",
+        "version" : "버전",
+        "stop" : "중지",
+        "close" : "닫음",
+        "open" : "염",
+        "agreement" : "동의",
+        "template" : "틀",
+        "category" : "분류",
+        "file" : "파일",
+        "writer" : "작성자",
+        "editor" : "에디터",
+        "hide" : "숨김",
+        "check" : "검사",
+        "destruction" : "삭제",
+        "tool" : "도구",
+        "recent" : "최근",
+        "password" : "비밀번호",
+        "login" : "로그인",
+        "logout" : "로그아웃",
+        "register" : "회원가입",
+        "language" : "언어",
+        "compare" : "비교",
+        "email" : "이메일",
+        "key" : "키",
+        "all" : "전체",
+        "sub" : "하위",
+        "create" : "생성",
+        "acl" : "ACL",
+        "upper" : "상위",
+        "other" : "기타",
+        "random" : "랜덤",
+        "error" : "오류",
+        "next" : "다음",
+        "previous" : "이전",
+        "authority" : "권한",
+        "connect" : "연결",
+        "_comment_1.1_" : "시간",
+            "second" : "초",
+            "hour" : "시간",
+            "limitless" : "무제한",
+            "time" : "시간",
+            "period" : "기간",
+            "end" : "끝",
+        "_comment_1.2_" : "사용자",
+            "user" : "사용자",
+            "admin" : "관리자",
+            "owner" : "소유자",
+            "ip" : "IP",
+            "member" : "가입자",
+        "_comment_1.3_" : "차단",
+            "ban" : "차단",
+            "blocked" : "차단됨",
+            "release" : "차단 해제",
+    "_comment_2_" : "기능",
+        "wiki_restart" : "위키 엔진 재시작",
+        "update" : "업데이트",
+        "need_document" : "필요한 문서들",
+        "close_discussion" : "닫힌 토론",
+        "open_discussion" : "열린 토론",
+        "recent_discussion" : "최근 토론",
+        "recent_change" : "최근 수정",
+        "edit_filter" : "편집 필터",
+        "recent_ban" : "최근 차단",
+        "load" : "다른 문서 불러오기",
+        "edit_filter_rule" : "편집 필터 규칙",
+        "move_history" : "이동 기록",
+        "other_tool" : "기타 도구",
+        "admin_tool" : "관리 도구",
+        "check_user" : "사용자 검사",
+        "compare_target" : "비교 대상 이름",
+        "authorize" : "인증",
+        "indexing" : "DB 인덱싱",
+        "hide_release" : "표시",
+        "notice_release" : "릴리즈 노트",
+        "ban_release" : "차단 해제",
+        "discussion_tool" : "토론 도구",
+        "discussion_raw" : "토론 원본",
+        "oauth_signin_facebook" : "Facebook 아이디로 로그인",
+        "oauth_signin_naver" : "네이버 아이디로 로그인",
+        "connection" : "연결",
+        "new_connection" : "연결...",
+        "user_setting" : "사용자 설정",
+        "now_password" : "현재 비밀번호",
+        "new_password" : "새 비밀번호",
+        "password_confirm" : "비밀번호 확인",
+        "oauth_connection" : "Oauth 연결",
+        "password_search" : "비밀번호 찾기",
+        "login_able" : "로그인 가능",
+        "band_ban" : "대역 차단",
+        "band_blocked" : "대역 차단됨",
+        "document_acl" : "문서 ACL",
+        "discussion_acl" : "토론 ACL",
+        "view_acl" : "읽기 ACL",
+        "under_category" : "하위 분류",
+        "count" : "기여 횟수",
+        "alarm" : "알림",
+        "user_document" : "사용자 문서",
+        "user_head" : "사용자 <head>",
+        "user_document_acl" : "사용자 문서 ACL",
+        "_comment_2.1_" : "필터",
+            "_comment_2.1.1_" : "목록",
+                "interwiki_list" : "인터위키 목록",
+                "email_filter_list" : "이메일 필터 목록",
+                "id_filter_list" : "ID 필터 목록",
+                "edit_filter_list" : "편집 필터 목록",
+            "_comment_2.1.2_" : "추가",
+                "interwiki_add" : "인터위키 추가",
+                "edit_filter_add" : "편집 필터 추가",
+                "id_filter_add" : "ID 필터 추가",
+                "email_filter_add" : "이메일 필터 추가",
+        "_comment_2.2_" : "설정",
+            "setting" : "설정",
+            "restart_required" : "재시작 필요",
+            "oauth_setting" : "OAuth 설정",
+            "adsense_setting" : "애드센스 설정",
+            "adsense_enable" : "애드센스 사용",
+            "skin_setting" : "스킨 설정",
+            "_comment_2.2.1_" : "목록",
+                "main_setting" : "메인 설정",
+                "text_setting" : "문자 설정",
+                "main_head" : "전역 <head>",
+                "main_body" : "전역 <body>",
+            "_comment_2.2.2_" : "메인",
+                "wiki_name" : "위키 이름",
+                "wiki_logo" : "위키 목록",
+                "main_page" : "대문",
+                "bottom_text" : "하단 텍스트",
+                "max_file_size" : "파일 최대 파일 크기",
+                "backup_interval" : "백업 주기",
+                "wiki_skin" : "위키 스킨",
+                "default_acl" : "기본 수정 ACL",
+                "default_discussion_acl" : "기본 토론 ACL",
+                "no_register" : "가입불가",
+                "hide_ip" : "IP 숨기기",
+                "wiki_host" : "위키 호스트",
+                "wiki_port" : "위키 포트",
+                "wiki_secret_key" : "위키 비밀키",
+                "email_required" : "이메일 필요",
+                "google_imap_required" : "Google IMAP 설정 필요",
+                "update_branch" : "업데이트를 가져올 브랜치",
+            "_comment_2.2.3_" : "문자열",
+                "register_text" : "회원가입 정책",
+                "non_login_alert" : "비로그인 알림",
+            "_comment_2.2.4_" : "Google",
+                "recaptcha" : "reCAPTCHA",
+                "google_imap" : "Google IMAP",
+                "google_email" : "Google 이매일",
+                "google_app_password" : "Google 앱 비밀번호",
+        "_comment_2.3_" : "목록",
+            "open_discussion_list" : "열린 토론 목록",
+            "discussion_list" : "토론 목록",
+            "admin_list" : "관리자 목록",
+            "member_list" : "사용자 목록",
+            "authority_use_list" : "권한 목록",
+            "admin_group_list" : "관리자 그룹 목록",
+            "all_document_list" : "모든 문서 목록",
+            "watchlist" : "주시 목록",
+            "_comment_2.3.1_" : "ACL 문서 목록",
+                "acl_document_list" : "ACL 문서 목록",
+                "acl_required" : "ACL 필요",
+            "_comment_2.3.2_" : "ACL 목록",
+                "admin_group_add" : "관리자 그룹 추가",
+                "ban_authority" : "차단 권한",
+                "discussion_authority" : "토론 관리 권한",
+                "user_check_authority" : "사용자 검사 권한",
+                "document_acl_authority" : "문서 ACL 관리 권한",
+                "history_hide_authority" : "역사 숨김 권한",
+                "authorization_authority" : "인증 권한",
+                "owner_authority" : "소유자 권한",
+            "_comment_2.3.3_" : "기록",
+                "edit_record" : "편집 기록",
+                "discussion_record" : "토론 기록",
+    "_comment_3_" : "장문",
+        "ie_no_data_required" : "이 기능을 수행하는데 필요한 데이터가 제공되지 않았습니다.",
+        "oauth_settings_not_found" : "관리자가 이 기능을 수행하는데 필요한 데이터를 제공하지 않았습니다.",
+        "oauth_disabled" : "관리자가 이 기능을 비활성화시켰습니다.",
+        "http_warring" : "경고: HTTPS 연결을 사용하지 않는다면 개인정보가 유출될 수 있습니다. 이 사항에 의해 입는 피해는 사용자에게 책임이 있음을 알려드립니다.",
+        "user_head_warring" : "비로그인시 브라우저를 닫거나 로그인시 사용자의 <head>는 삭제됩니다.",
+        "no_login_warring" : "비로그인 상태입니다. 편집시 지금 접속한 IP 명의로 기록됩니다.",
+        "_comment_3.1_" : "오류",
+            "update_error" : "자동 업데이트가 지원되지 않습니다.",
+            "inter_error" : "내부 오류.",
+            "authority_error" : "권한이 없습니다.",
+            "no_login_error" : "비로그인 상태입니다.",
+            "no_exist_user_error" : "계정이 존재하지 않습니다.",
+            "no_admin_block_error" : "관리자는 검사, 차단을 수행할 수 없습니다.",
+            "skin_error" : "이 스킨은 설정을 지원하지 않습니다.",
+            "same_id_exist_error" : "이미 같은 이름의 계정이 있습니다.",
+            "long_id_error" : "ID는 20자보다 짧아야 합니다.",
+            "id_char_error" : "오직 한글과 알파벳, 공백만 사용 가능합니다.",
+            "file_exist_error" : "파일이 존재하지 않습니다.",
+            "password_error" : "비밀번호가 다릅니다.",
+            "recaptcha_error" : "'나는 로봇이 아닙니다'를 통해 reCaptcha를 수행하세요.",
+            "file_extension_error" : "오직 jpg, gif, jpeg, png, webp 만 업로드할 수 있습니다.",
+            "edit_record_error" : "수정 요약은 500자를 넘길 수 없습니다.",
+            "same_file_error" : "똑같은 이름의 파일이 존재합니다.",
+            "file_capacity_error" : "최대 파일 크기 (MB) :",
+            "decument_exist_error" : "이동하려는 이름에 이미 문서가 존재합니다.",
+            "password_diffrent_error" : "입력한 비밀번호와 비밀번호 확인이 서로 다릅니다.",
+            "edit_filter_error" : "편집 필터에 의해 금지된 단어가 사용되었습니다.",
+            "file_name_error" : "파일 이름에는 알파벳, 한글, 공백, 밑줄 과 빼기 기호만 사용할 수 있습니다."
 }
 }

+ 0 - 18
language/test.py

@@ -1,18 +0,0 @@
-import json
-
-print('name : ', end = '')
-b = input()
-
-json_data = open('en-US.json', 'rt', encoding='utf-8').read()
-a_json = json.loads(json_data)
-
-json_data = open(str(b) + '.json', 'rt', encoding='utf-8').read()
-b_json = json.loads(json_data)
-
-for a_in in a_json:
-    if not a_in in b_json:
-        print(a_in + ' : ', end = '')
-        c = input()
-        b_json[a_in] = c
-        
-print(str(b_json).replace('", ', '",\n    ').replace('{', '{\n    ').replace('}', '\n}').replace('\'', '"'))

+ 36 - 37
readme-ko.md

@@ -1,58 +1,57 @@
-opennamu
+openNAMU
 ====
 ====
-![Python 3.5 이상 필요](https://img.shields.io/badge/python-%3E%3D%203.5-blue.svg)
+[![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)
 
 
 오픈나무는 파이썬 기반의 위키 엔진입니다. 파이썬과 그 의존성 모듈만 설치하면 사용할 수 있으며, 코드를 직접 수정하여 좀 더 주제에 특화된 위키를 만들 수 있습니다.
 오픈나무는 파이썬 기반의 위키 엔진입니다. 파이썬과 그 의존성 모듈만 설치하면 사용할 수 있으며, 코드를 직접 수정하여 좀 더 주제에 특화된 위키를 만들 수 있습니다.
 
 
 ### 목차
 ### 목차
- * [시작하기](#시작하기)
- * [클론](#클론)
- * [기여](#기여)
- * [라이선스](#라이선스)
- * [기여자 목록](#기여자-목록)
- * [기타](#기타)
+[클론](#클론) | [기여](#기여) | [라이선스](#라이선스) | [기여자 목록](#기여자-목록) | [기타](#기타)
+
+## 시작하기
+오픈나무는 파이썬 환경에서 동작하는 파이썬 애플리케이션으로, 파이썬 환경을 필요로 합니다.
+
+쉬운 오픈나무 설치를 위해 오픈나무 가이드를 따로 생성해두었으며, [이곳](https://github.com/Make-openNAMU/guide)에서 확인하실 수 있습니다.
 
 
-# 시작하기
- * 오픈나무 위키에 명시되어 있습니다. [참조](http://namu.ml/w/오픈나무%2F설치법)
+### 가이드 목록
+ * [파이썬 설치](https://github.com/Make-openNAMU/guide/blob/master/articles/ko-kr/install-python.md)
+ * [오픈나무 시작](https://github.com/Make-openNAMU/guide/blob/master/articles/ko-kr/start-opennamu.md)
+   * [오픈나무 도커 시작(실험적 기능)](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 stable https://github.com/2du/openNAMU.git`
 
 
-## 베타
- * `git clone -b master https://github.com/2du/opennamu.git`
+### 개발중
+ * `git clone -b master https://github.com/2du/openNAMU.git`
 
 
-# 기여
+## 기여
 오픈나무에는 검증되지 않은 몇가지 버그가 존재할 수 있습니다. 당신의 오픈나무 사용과 버그 발견은 오픈나무의 발전을 돕습니다.
 오픈나무에는 검증되지 않은 몇가지 버그가 존재할 수 있습니다. 당신의 오픈나무 사용과 버그 발견은 오픈나무의 발전을 돕습니다.
-[이슈 생성하기](https://github.com/2du/opennamu/issues/new)
+[이슈 생성하기](https://github.com/2du/openNAMU/issues/new)
 
 
-오픈나무는 완전한 오픈소스 프로젝트입니다. 새로운 기능을 추가하고 Pull Request를 해보세요. [다음 절차]에 따라 기여할 수 있습니다.
-[Pull Requests 생성하기](https://github.com/2du/opennamu/compare)
+오픈나무는 완전한 오픈소스 프로젝트입니다. 새로운 기능을 추가하고 Pull Request를 생성해보세요.
+[Pull Request 생성하기](https://github.com/2du/openNAMU/compare)
 
 
-# 라이선스
-오픈나무는 [BSD 3-Clause License](./LICENSE)에 의해 보호받고 있습니다. 자세한 내용은 문서를 참고하세요.
+## 라이선스
+openNAMU 프로젝트는 [BSD 3-Clause License](./LICENSE)(이하 이용허락)의 보호를 받고 있으며, openNAMU 프로젝트를 사용하고자 한다면 를 준수해야 합니다. 본 이용허락를 위반할 경우 개발자는 DMCA Takedown 등 관련 제재를 관계자에게 요청할 권리가 있으며, 그 책임은 모두 이용허락 위반 사용자에게 있습니다. 자세한 내용은 문서를 참고하세요.
 
 
-## 외부 프로젝트 라이선스
- * Quotes icon [Dave Gandy](http://www.flaticon.com/free-icon/quote-left_25672) CC 3.0 BY
- * 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) - CC 3.0 BY
+ * Syntax highlighting - [highlightjs](https://highlightjs.org/) - [BSD License](https://github.com/highlightjs/highlight.js/blob/master/LICENSE)
+ * Numerical expression - [MathJax](https://www.mathjax.org/) - [Apache License 2.0](https://github.com/mathjax/MathJax/blob/master/LICENSE)
 
 
-# 기여자 목록
- * [참고](https://github.com/2DU/opennamu/graphs/contributors)
+## 기여자 목록
+ * [참고](https://github.com/2DU/openNAMU/graphs/contributors)
 
 
-## 도움을 주신 분들
+### 도움을 주신 분들
  * [Team Croatia](https://github.com/TeamCroatia)
  * [Team Croatia](https://github.com/TeamCroatia)
  * Basix
  * Basix
  * Efrit
  * Efrit
  * Other chat rooms
  * Other chat rooms
 
 
-# 기타
-`set.json`은 몇가지 로컬 설정을 저장하는 설정 파일입니다.
- * db = 데이터베이스 이름
-
-`set.json`은 삭제해도 다시 새로 만들 수 있습니다.
-
-[테스트 서버](http://namu.ml/)
-
-첫 번째 가입자에게 소유자 권한이 부여됩니다.
+## 기타
+ * [테스트 서버](http://namu.ml/)
+ * 첫 가입자에게 소유자 권한이 부여됩니다.

+ 49 - 21
readme.md

@@ -1,10 +1,13 @@
-opennamu
+openNAMU
 ====
 ====
-![Python 3.5 or later Required](https://img.shields.io/badge/python-%3E%3D%203.5-blue.svg)
+[![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)
 
 
-opennamu is a Python-based wiki engine. If you install Python and its underlying modules, you will be able to create wikis.
+![](https://raw.githubusercontent.com/2du/openNAMU/master/.github/logo.png)
 
 
- * [(README for korean)](./readme-ko.md)
+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)
 
 
 ### Index
 ### Index
  * [Getting Started](#getting-started)
  * [Getting Started](#getting-started)
@@ -12,27 +15,58 @@ opennamu is a Python-based wiki engine. If you install Python and its underlying
  * [Contribute](#contribute)
  * [Contribute](#contribute)
  * [License](#license)
  * [License](#license)
  * [Authors](#authors)
  * [Authors](#authors)
- * [Etc.](#etc.)
+ * [Etc.](#etc)
 
 
 # Getting Started
 # Getting Started
+openNAMU is based upon Python, and it requires a Python environment.
+
+## 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
 # Clone
 You can clone this repository by entering the following command at the terminal (command prompt):
 You can clone this repository by entering the following command at the terminal (command prompt):
 ## Stable
 ## Stable
- * `git clone -b stable https://github.com/2du/opennamu.git`
+ * `git clone -b stable https://github.com/2du/openNAMU.git`
 
 
 ## Beta
 ## Beta
- * `git clone -b master https://github.com/2du/opennamu.git`
+ * `git clone -b master https://github.com/2du/openNAMU.git`
 
 
 # Contribute
 # 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 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)
+openNAMU is open source project. Add new features and request pull requests. 
+[Create Pull Requests](https://github.com/2du/openNAMU/compare)
 
 
 # Lisence
 # Lisence
-opennamu is protected by [BSD 3-Clause License](./LICNESE). Please refer to the documentation for details.
+openNAMU is protected by [BSD 3-Clause License](./LICNESE). Please refer to the documentation for details.
 
 
 ## External Projects
 ## External Projects
  * Quotes icon [Dave Gandy](http://www.flaticon.com/free-icon/quote-left_25672) CC 3.0 BY
  * Quotes icon [Dave Gandy](http://www.flaticon.com/free-icon/quote-left_25672) CC 3.0 BY
@@ -40,7 +74,7 @@ opennamu is protected by [BSD 3-Clause License](./LICNESE). Please refer to the
  * Numerical expression [MathJax](https://www.mathjax.org/)
  * Numerical expression [MathJax](https://www.mathjax.org/)
 
 
 # Authors
 # Authors
- * [Reference](https://github.com/2DU/opennamu/graphs/contributors)
+ * [Reference](https://github.com/2DU/openNAMU/graphs/contributors)
 
 
 ## Special Thanks
 ## Special Thanks
  * [Team Croatia](https://github.com/TeamCroatia)
  * [Team Croatia](https://github.com/TeamCroatia)
@@ -49,11 +83,5 @@ opennamu is protected by [BSD 3-Clause License](./LICNESE). Please refer to the
  * Other chat rooms
  * Other chat rooms
 
 
 # Etc.
 # Etc.
-`set.json` is a configuration file that stores some local settings.
- * db = Database name
-
-If you delete `set.json`, you can create a new one again.
-
-[Test Server](http://namu.ml/)
-
-Owner rights are granted to the first registor.
+ * [Test Server](http://namu.ml/)
+ * Owner rights are granted to the first registor.

+ 117 - 0
route/acl.py

@@ -0,0 +1,117 @@
+from .tool.func import *
+
+def acl_2(conn, name):
+    curs = conn.cursor()
+
+    check_ok = ''
+    
+    if flask.request.method == 'POST':
+        check_data = 'acl (' + name + ')'
+    else:
+        check_data = None
+    
+    user_data = re.search('^user:(.+)$', name)
+    if user_data:
+        if check_data and custom()[2] == 0:
+            return redirect('/login')
+        
+        if user_data.groups()[0] != ip_check():
+            if admin_check(5, check_data) != 1:
+                if check_data:
+                    return re_error('/error/3')
+                else:
+                    check_ok = 'disabled'
+    else:
+        if admin_check(5, check_data) != 1:
+            if check_data:
+                return re_error('/error/3')
+            else:
+                check_ok = 'disabled'
+
+    if flask.request.method == 'POST':
+        if flask.request.form.get('dec', '') != flask.request.form.get('view', ''):
+            dec = flask.request.form.get('view', '')
+            view = flask.request.form.get('view', '')
+        else:
+            dec = flask.request.form.get('dec', '')
+            view = flask.request.form.get('view', '')
+
+        curs.execute("select title from acl where title = ?", [name])
+        if curs.fetchall():
+            curs.execute("update acl set dec = ? where title = ?", [dec, 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])
+        else:
+            curs.execute("insert into acl (title, dec, dis, why, view) values (?, ?, ?, ?, ?)", [name, dec, flask.request.form.get('dis', ''), flask.request.form.get('why', ''), view])
+        
+        curs.execute("select title from acl where title = ? and dec = '' and dis = ''", [name])
+        if curs.fetchall():
+            curs.execute("delete from acl where title = ?", [name])
+
+        conn.commit()
+            
+        return redirect('/acl/' + url_pas(name))            
+    else:
+        data = '' + load_lang('document_acl') + '<br><br><select name="dec" ' + check_ok + '>'
+    
+        if re.search('^user:', name):
+            acl_list = [['', load_lang('normal')], ['user', load_lang('member')], ['all', load_lang('all')]]
+        else:
+            acl_list = [['', load_lang('normal')], ['user', load_lang('member')], ['admin', load_lang('admin')]]
+        
+        curs.execute("select dec 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]:
+                check = 'selected="selected"'
+            else:
+                check = ''
+            
+            data += '<option value="' + data_list[0] + '" ' + check + '>' + data_list[1] + '</option>'
+            
+        data += '</select>'
+        
+        if not re.search('^user:', name):
+            data += '<hr class=\"main_hr\">' + load_lang('discussion_acl') + '<br><br><select name="dis" ' + check_ok + '>'
+        
+            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]:
+                    check = 'selected="selected"'
+                else:
+                    check = ''
+                    
+                data += '<option value="' + data_list[0] + '" ' + check + '>' + data_list[1] + '</option>'
+                
+            data += '</select>'
+
+            data += '<hr class=\"main_hr\">' + load_lang('view_acl') + '<br><br><select name="view" ' + check_ok + '>'
+            for data_list in acl_list:
+                if acl_data and acl_data[0][2] == data_list[0]:
+                    check = 'selected="selected"'
+                else:
+                    check = ''
+                    
+                data += '<option value="' + data_list[0] + '" ' + check + '>' + data_list[1] + '</option>'
+                
+            data += '</select>'
+                
+            if check_ok == '':
+                if acl_data:
+                    data += '<hr class=\"main_hr\"><input value="' + html.escape(acl_data[0][1]) + '" placeholder="' + load_lang('why') + '" name="why" type="text" ' + check_ok + '>'
+                else:
+                    data += '<hr class=\"main_hr\"><input placeholder="' + load_lang('why') + '" name="why" type="text" ' + check_ok + '>'
+            
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [name, wiki_set(), custom(), other2([' (' + load_lang('acl') + ')', 0])],
+            data =  '''
+                <form method="post">
+                    ''' + data + '''
+                    <hr class=\"main_hr\">
+                    <button type="submit" ''' + check_ok + '''>''' + load_lang('save') + '''</button>
+                </form>
+            ''',
+            menu = [['w/' + url_pas(name), load_lang('document')], ['manager', load_lang('admin')]]
+        ))

+ 47 - 0
route/acl_list.py

@@ -0,0 +1,47 @@
+from .tool.func import *
+
+def acl_list_2(conn):
+    curs = conn.cursor()
+    
+    div =   '''
+        <table id="main_table_set">
+            <tbody>
+                <tr>
+                    <td id="main_table_width_quarter">''' + load_lang('document_name') + '''</td>
+                    <td id="main_table_width_quarter">''' + load_lang('document') + ''' acl</td>
+                    <td id="main_table_width_quarter">''' + load_lang('discussion') + ''' acl</td>
+                    <td id="main_table_width_quarter">''' + load_lang('acl_required') + '''</td>
+    '''
+    
+    curs.execute("select title, dec, dis, view, why from acl where dec = 'admin' or dec = 'user' or dis = 'admin' or dis = 'user' or view = 'admin' or view = 'user' order by title desc")
+    list_data = curs.fetchall()
+    for data in list_data:
+        if not re.search('^user:', data[0]) and not re.search('^file:', data[0]):
+            acl = []
+            for i in range(1, 4):
+                if data[i] == 'admin':
+                    acl += [load_lang('admin')]
+                else:
+                    acl += [load_lang('member')]
+
+            div +=  '''
+                <tr>
+                    <td>
+                        <a href="/w/''' + url_pas(data[0]) + '">' + data[0] + '''</a>
+                    </td>
+                    <td>''' + acl[0] + '''</td>
+                    <td>''' + acl[1] + '''</td>
+                    <td>''' + acl[2] + '''</td>
+                </tr>
+            '''
+        
+    div +=  '''
+            </tbody>
+        </table>
+    '''
+    
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [load_lang('acl_document_list'), wiki_set(), custom(), other2([0, 0])],
+        data = div,
+        menu = [['other', load_lang('return')]]
+    ))

+ 23 - 0
route/admin_list.py

@@ -0,0 +1,23 @@
+from .tool.func import *
+
+def admin_list_2(conn):
+    curs = conn.cursor()
+
+    div = '<ul>'
+    
+    curs.execute("select id, acl, date from user where not acl = 'user' order by date desc")
+    for data in curs.fetchall():
+        name = ip_pas(data[0]) + ' <a href="/admin_plus/' + url_pas(data[1]) + '">(' + data[1] + ')</a>'
+        
+        if data[2] != '':
+            name += '(' + data[2] + ')'
+
+        div += '<li>' + name + '</li>'
+        
+    div += '</ul>'
+                
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [load_lang('admin_list'), wiki_set(), custom(), other2([0, 0])],
+        data = div,
+        menu = [['other', load_lang('return')]]
+    ))

+ 26 - 0
route/admin_log.py

@@ -0,0 +1,26 @@
+from .tool.func import *
+
+def admin_log_2(conn):
+    curs = conn.cursor()
+    
+    num = int(number_check(flask.request.args.get('num', '1')))
+    if num * 50 > 0:
+        sql_num = num * 50 - 50
+    else:
+        sql_num = 0
+
+    list_data = '<ul>'
+
+    curs.execute("select who, what, time from re_admin order by time desc limit ?, '50'", [str(sql_num)])
+    get_list = curs.fetchall()
+    for data in get_list:            
+        list_data += '<li>' + ip_pas(data[0]) + ' / ' + data[1] + ' / ' + data[2] + '</li>'
+
+    list_data += '</ul>'
+    list_data += next_fix('/admin_log?num=', num, get_list)
+
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [load_lang('authority_use_list'), wiki_set(), custom(), other2([0, 0])],
+        data = list_data,
+        menu = [['other', load_lang('return')]]
+    ))

+ 85 - 0
route/admin_plus.py

@@ -0,0 +1,85 @@
+from .tool.func import *
+
+def admin_plus_2(conn):
+    curs = conn.cursor()
+    
+    if flask.request.method == 'POST':
+        if admin_check(None, 'admin_plus (' + name + ')') != 1:
+            return re_error('/error/3')
+
+        curs.execute("delete from alist where name = ?", [name])
+        
+        if flask.request.form.get('ban', 0) != 0:
+            curs.execute("insert into alist (name, acl) values (?, 'ban')", [name])
+
+        if flask.request.form.get('toron', 0) != 0:
+            curs.execute("insert into alist (name, acl) values (?, 'toron')", [name])
+            
+        if flask.request.form.get('check', 0) != 0:
+            curs.execute("insert into alist (name, acl) values (?, 'check')", [name])
+
+        if flask.request.form.get('acl', 0) != 0:
+            curs.execute("insert into alist (name, acl) values (?, 'acl')", [name])
+
+        if flask.request.form.get('hidel', 0) != 0:
+            curs.execute("insert into alist (name, acl) values (?, 'hidel')", [name])
+
+        if flask.request.form.get('give', 0) != 0:
+            curs.execute("insert into alist (name, acl) values (?, 'give')", [name])
+
+        if flask.request.form.get('owner', 0) != 0:
+            curs.execute("insert into alist (name, acl) values (?, 'owner')", [name])
+            
+        conn.commit()
+        
+        return redirect('/admin_plus/' + url_pas(name))
+    else:        
+        data = '<ul>'
+        
+        exist_list = ['', '', '', '', '', '', '', '']
+
+        curs.execute('select acl from alist where name = ?', [name])
+        acl_list = curs.fetchall()    
+        for go in acl_list:
+            if go[0] == 'ban':
+                exist_list[0] = 'checked="checked"'
+            elif go[0] == 'toron':
+                exist_list[2] = 'checked="checked"'
+            elif go[0] == 'check':
+                exist_list[3] = 'checked="checked"'
+            elif go[0] == 'acl':
+                exist_list[4] = 'checked="checked"'
+            elif go[0] == 'hidel':
+                exist_list[5] = 'checked="checked"'
+            elif go[0] == 'give':
+                exist_list[6] = 'checked="checked"'
+            elif go[0] == 'owner':
+                exist_list[7] = 'checked="checked"'
+
+        if admin_check() != 1:
+            state = 'disabled'
+        else:
+            state = ''
+
+        data += '''
+                    <li><input type="checkbox" ''' + state +  ' name="ban" ' + exist_list[0] + '> ' + load_lang('ban_authority') + '''</li>
+                    <li><input type="checkbox" ''' + state +  ' name="toron" ' + exist_list[2] + '> ' + load_lang('discussion_authority') + '''</li>
+                    <li><input type="checkbox" ''' + state +  ' name="check" ' + exist_list[3] + '> ' + load_lang('user_check_authority') + '''</li>
+                    <li><input type="checkbox" ''' + state +  ' name="acl" ' + exist_list[4] + '> ' + load_lang('document_acl_authority') + '''</li>
+                    <li><input type="checkbox" ''' + state +  ' name="hidel" ' + exist_list[5] + '> ' + load_lang('history_hide_authority') + '''</li>
+                    <li><input type="checkbox" ''' + state +  ' name="give" ' + exist_list[6] + '> ' + load_lang('authorization_authority') + '''</li>
+                    <li><input type="checkbox" ''' + state +  ' name="owner" ' + exist_list[7] + '> ' + load_lang('owner_authority') + '''</li>
+                </ul>
+                '''
+
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [load_lang('admin_group_add'), wiki_set(), custom(), other2([0, 0])],
+            data =  '''
+                    <form method="post">
+                        ''' + data + '''
+                        <hr class=\"main_hr\">
+                        <button id="save" ''' + state +  ''' type="submit">''' + load_lang('save') + '''</button>
+                    </form>
+                    ''',
+            menu = [['manager', load_lang('return')]]
+        ))     

+ 65 - 0
route/adsense_setting.py

@@ -0,0 +1,65 @@
+from .tool.func import *
+
+def adsense_setting_2(conn):
+    curs = conn.cursor()
+
+    if admin_check(None, 'adsense setting') != 1:
+        return re_error('/error/3')
+    
+    if flask.request.method == 'POST':
+        try:
+            adsense_enabled = flask.request.form.get('adsense_enabled')
+            adsense_code = flask.request.form['adsense_code']
+        except:
+            return easy_minify(flask.render_template(skin_check(),
+                imp = [load_lang('inter_error'), wiki_set(), custom(), other2([0, 0])],
+                data = '<h2>ie_no_data_required</h2>' + load_lang('ie_no_data_required'),
+                menu = [['other', load_lang('return')]]
+            ))
+        
+        if adsense_enabled == 'on':
+            curs.execute('update other set data = "True" where name = "adsense"')
+        else:
+            curs.execute('update other set data = "False" where name = "adsense"')
+        
+        curs.execute('update other set data = ? where name = "adsense_code"', [adsense_code])
+        conn.commit()
+        
+        return redirect('/adsense_setting')
+
+    body_content = ''
+
+    curs.execute('select data from other where name = "adsense"')
+    adsense_enabled = curs.fetchall()[0][0]
+
+    curs.execute('select data from other where name = "adsense_code"')
+    adsense_code = curs.fetchall()[0][0]
+
+    template = '''
+        <form action="" accept-charset="utf-8" method="post">
+            <div class="form-check">
+                <label class="form-check-label">
+                    <input class="form-check-input" name="adsense_enabled" type="checkbox" {}>
+                    {}
+                </label>
+            </div>
+            <hr>
+            <div class="form-group">
+                <textarea class="form-control" id="adsense_code" name="adsense_code" rows="12">{}</textarea>
+            </div>
+            <button type="submit" value="publish">{}</button>
+        </form>
+    '''
+    
+    body_content += template.format(
+        'checked' if adsense_enabled == 'True' else template.format(''),
+        load_lang('adsense_enable'),
+        load_lang('save'),
+        adsense_code
+    )
+
+    return easy_minify(flask.render_template(skin_check(),
+        imp = [load_lang('adsense_setting'), wiki_set(), custom(), other2([0, 0])],
+        data = body_content,
+        menu = [['other', load_lang('return')]]
+    ))

+ 25 - 0
route/alarm.py

@@ -0,0 +1,25 @@
+from .tool.func import *
+
+def alarm_2(conn):
+    curs = conn.cursor()
+    
+    if custom()[2] == 0:
+        return redirect('/login')    
+
+    data = '<ul>'    
+    
+    curs.execute("select data, date from alarm where name = ? order by date desc", [ip_check()])
+    data_list = curs.fetchall()
+    if data_list:
+        data = '<a href="/del_alarm">(' + load_lang('delete') + ')</a><hr class=\"main_hr\">' + data
+
+        for data_one in data_list:
+            data += '<li>' + data_one[0] + ' (' + data_one[1] + ')</li>'
+    
+    data += '</ul>'
+
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [load_lang('notice'), wiki_set(), custom(), other2([0, 0])],
+        data = data,
+        menu = [['user', load_lang('return')]]
+    ))

+ 91 - 0
route/block_log.py

@@ -0,0 +1,91 @@
+from .tool.func import *
+
+def block_log_2(conn, name, tool):
+    curs = conn.cursor()
+
+    num = int(number_check(flask.request.args.get('num', '1')))
+    if num * 50 > 0:
+        sql_num = num * 50 - 50
+    else:
+        sql_num = 0
+    
+    div =   '''
+            <table id="main_table_set">
+                <tbody>
+                    <tr>
+                        <td id="main_table_width">''' + load_lang('blocked') + '''</td>
+                        <td id="main_table_width">''' + load_lang('admin') + '''</td>
+                        <td id="main_table_width">''' + load_lang('period') + '''</td>
+                    </tr>
+            '''
+    
+    data_list = ''
+    
+    if not name:
+        div =   '''
+                <a href="/manager/11">(''' + load_lang('blocked') + ''')</a> <a href="/manager/12">(''' + load_lang('admin') + ''')</a>
+                <hr class=\"main_hr\">
+                ''' + div
+        
+        sub = 0
+        menu = 0
+        
+        curs.execute("select why, block, blocker, end, today from rb order by today desc limit ?, '50'", [str(sql_num)])
+    else:
+        menu = [['block_log', load_lang('normal')]]
+        
+        if tool == 'block_user':
+            sub = ' (' + load_lang('blocked') + ')'
+            
+            curs.execute("select why, block, blocker, end, today from rb where block = ? order by today desc limit ?, '50'", [name, str(sql_num)])
+        else:
+            sub = ' (' + load_lang('admin') + ')'
+            
+            curs.execute("select why, block, blocker, end, today from rb where blocker = ? order by today desc limit ?, '50'", [name, str(sql_num)])
+
+    if data_list == '':
+        data_list = curs.fetchall()
+
+    for data in data_list:
+        why = html.escape(data[0])
+        if why == '':
+            why = '<br>'
+        
+        band = re.search("^([0-9]{1,3}\.[0-9]{1,3})$", data[1])
+        if band:
+            ip = data[1] + ' (' + load_lang('range') + ')'
+        else:
+            ip = ip_pas(data[1])
+
+        if data[3] != '':
+            end = data[3]
+        else:
+            end = load_lang('limitless') + ''
+            
+        div +=  '''
+            <tr>
+                <td>''' + ip + '''</td>
+                <td>''' + ip_pas(data[2]) + '''</td>
+                <td>
+                    start : ''' + data[4] + '''
+                    <br>
+                    end : ''' + end + '''
+                </td>
+            </tr>
+            <tr>
+                <td colspan="3">''' + why + '''</td>
+            </tr>
+        '''
+
+    div += '</tbody></table>'
+    
+    if not name:
+        div += next_fix('/block_log?num=', num, data_list)
+    else:
+        div += next_fix('/' + url_pas(tool) + '/' + url_pas(name) + '?num=', num, data_list)
+                
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [load_lang('recent_ban'), wiki_set(), custom(), other2([sub, 0])],
+        data = div,
+        menu = menu
+    ))

+ 120 - 0
route/change_password.py

@@ -0,0 +1,120 @@
+from .tool.func import *
+
+def change_password_2(conn):
+    curs = conn.cursor()
+
+    support_language = server_init.server_set_var['language']['list']
+
+    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 flask.request.method == 'POST':    
+            if flask.request.form.get('pw4', None) and flask.request.form.get('pw2', None):
+                if flask.request.form.get('pw2', None) != flask.request.form.get('pw3', None):
+                    return re_error('/error/20')
+
+                curs.execute("select pw, encode from user where id = ?", [flask.session['id']])
+                user = curs.fetchall()
+                if not user:
+                    return re_error('/error/2')
+                
+                pw_check_d = pw_check(
+                    flask.request.form.get('pw4', ''), 
+                    user[0][0],
+                    user[0][1],
+                    flask.request.form.get('id', None)
+                )
+                if pw_check_d != 1:
+                    return re_error('/error/10')
+
+                hashed = pw_encode(flask.request.form.get('pw2', None))
+                
+                curs.execute("update user set pw = ? where id = ?", [hashed, flask.session['id']])
+
+            auto_list = ['email', 'skin', 'lang']
+
+            for auto_data in auto_list:
+                curs.execute('select data from user_set where name = ? and id = ?', [auto_data, ip])
+                if curs.fetchall():
+                    curs.execute("update user_set set data = ? where name = ? and id = ?", [flask.request.form.get(auto_data, ''), auto_data, ip])
+                else:
+                    curs.execute("insert into user_set (name, id, data) values (?, ?, ?)", [auto_data, ip, flask.request.form.get(auto_data, '')])
+
+            conn.commit()
+            
+            return redirect('/change')
+        else:        
+            curs.execute('select data from user_set where name = "email" and id = ?', [ip])
+            data = curs.fetchall()
+            if data:
+                email = data[0][0]
+            else:
+                email = ''
+
+            div2 = load_skin()
+            
+            div3 = ''
+            var_div3 = ''
+
+            curs.execute('select data from user_set where name = "lang" and id = ?', [flask.session['id']])
+            data = curs.fetchall()
+
+            for lang_data in support_language:
+                if data and data[0][0] == lang_data:
+                    div3 = '<option value="' + lang_data + '">' + lang_data + '</option>'
+                else:
+                    var_div3 += '<option value="' + lang_data + '">' + lang_data + '</option>'
+
+            div3 += var_div3
+
+            oauth_provider = load_oauth('_README')['support']
+            oauth_content = '<ul>'
+            for i in range(len(oauth_provider)):
+                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], load_lang('connection') + ' : <img src="{}" width="17px" height="17px">{}'.format(oauth_data[0][1], oauth_data[0][0]))
+                else:
+                    oauth_content += '<li>{} - {}</li>'.format(oauth_provider[i], load_lang('connection') + ' : <a href="/oauth/{}/init">{}</a>'.format(oauth_provider[i], load_lang('connect')))
+            
+            oauth_content += '</ul>'
+
+            return easy_minify(flask.render_template(skin_check(),    
+                imp = [load_lang('user_setting'), wiki_set(), custom(), other2([0, 0])],
+                data =  '''
+                        <form method="post">
+                            <span>id : ''' + ip + '''</span>
+                            <hr class=\"main_hr\">
+                            <input placeholder="''' + load_lang('now_password') + '''" name="pw4" type="password">
+                            <hr class=\"main_hr\">
+                            <input placeholder="''' + load_lang('new_password') + '''" name="pw2" type="password">
+                            <hr class=\"main_hr\">
+                            <input placeholder="''' + load_lang('password_confirm') + '''" name="pw3" type="password">
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('skin') + '''</span>
+                            <hr class=\"main_hr\">
+                            <select name="skin">''' + div2 + '''</select>
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('language') + '''</span>
+                            <hr class=\"main_hr\">
+                            <select name="lang">''' + div3 + '''</select>
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('oauth_connection') + '''</span>
+                            ''' + oauth_content + '''
+                            <hr class=\"main_hr\">
+                            <button type="submit">''' + load_lang('save') + '''</button>
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('http_warring') + '''</span>
+                        </form>
+                        ''',
+                menu = [['user', load_lang('return')]]
+            ))
+    else:
+        pass

+ 83 - 0
route/check_key.py

@@ -0,0 +1,83 @@
+from .tool.func import *
+
+def check_key_2(conn, tool):
+    curs = conn.cursor()
+
+    if flask.request.method == 'POST':
+        if tool == 'check_key':
+            if 'c_id' in flask.session and flask.session['c_key'] == flask.request.form.get('key', None):
+                curs.execute('select data from other where name = "encode"')
+                db_data = curs.fetchall()
+
+                curs.execute("select id from user limit 1")
+                if not curs.fetchall():
+                    curs.execute("insert into user (id, pw, acl, date, encode) values (?, ?, 'owner', ?, ?)", [flask.session['c_id'], flask.session['c_pw'], get_time(), db_data[0][0]])
+
+                    first = 1
+                else:
+                    curs.execute("insert into user (id, pw, acl, date, encode) values (?, ?, 'user', ?, ?)", [flask.session['c_id'], flask.session['c_pw'], get_time(), db_data[0][0]])
+
+                    first = 0
+
+                ip = ip_check()
+                agent = flask.request.headers.get('User-Agent')
+
+                curs.execute("insert into user_set (name, id, data) values ('email', ?, ?)", [flask.session['c_id'], flask.session['c_email']])
+                curs.execute("insert into ua_d (name, ip, ua, today, sub) values (?, ?, ?, ?, '')", [flask.session['c_id'], ip, agent, get_time()])
+
+                flask.session['state'] = 1
+                flask.session['id'] = flask.session['c_id']
+                flask.session['head'] = ''
+                        
+                conn.commit()
+                
+                flask.session.pop('c_id', None)
+                flask.session.pop('c_pw', None)
+                flask.session.pop('c_key', None)
+                flask.session.pop('c_email', None)
+
+                if first == 0:
+                    return redirect('/change')
+                else:
+                    return redirect('/setting')
+            else:
+                flask.session.pop('c_id', None)
+                flask.session.pop('c_pw', None)
+                flask.session.pop('c_key', None)
+                flask.session.pop('c_email', None)
+
+                return redirect('/register')
+        else:
+            if 'c_id' in flask.session and flask.session['c_key'] == flask.request.form.get('key', None):
+                hashed = pw_encode(flask.session['c_key'])
+                curs.execute("update user set pw = ? where id = ?", [hashed, flask.session['c_id']])
+
+                d_id = flask.session['c_id']
+                pw = flask.session['c_key']
+
+                flask.session.pop('c_id', None)
+                flask.session.pop('c_key', None)
+
+                return easy_minify(flask.render_template(skin_check(),    
+                    imp = ['check', wiki_set(), custom(), other2([0, 0])],
+                    data =  '''
+                            ''' + load_lang('id') + ' : ' + d_id + '''
+                            <br>
+                            ''' + load_lang('password') + ' : ' + pw + '''
+                            ''',
+                    menu = [['user', load_lang('return')]]
+                ))
+            else:
+                return redirect('/pass_find')
+    else:
+        return easy_minify(flask.render_template(skin_check(),    
+            imp = ['check', wiki_set(), custom(), other2([0, 0])],
+            data =  '''
+                    <form method="post">
+                        <input placeholder="''' + load_lang('key') + '''" name="key" type="text">
+                        <hr class=\"main_hr\">
+                        <button type="submit">''' + load_lang('save') + '''</button>
+                    </form>
+                    ''',
+            menu = [['user', load_lang('return')]]
+        ))

+ 69 - 0
route/close_topic_list.py

@@ -0,0 +1,69 @@
+from .tool.func import *
+
+def close_topic_list_2(conn, name, tool):
+    curs = conn.cursor()
+    
+    div = ''
+    
+    if flask.request.method == 'POST':
+        t_num = ''
+        
+        while 1:
+            curs.execute("select title from topic where title = ? and sub = ? limit 1", [name, flask.request.form.get('topic', None) + t_num])
+            if curs.fetchall():
+                if t_num == '':
+                    t_num = ' 2'
+                else:
+                    t_num = ' ' + str(int(t_num.replace(' ', '')) + 1)
+            else:
+                break
+
+        return redirect('/topic/' + url_pas(name) + '/sub/' + url_pas(flask.request.form.get('topic', None) + t_num))
+    else:
+        plus = ''
+        menu = [['topic/' + url_pas(name), load_lang('return')]]
+        
+        if tool == 'close':
+            curs.execute("select sub from rd where title = ? and stop = 'O' order by sub asc", [name])
+            
+            sub = load_lang('close') + ''
+        elif tool == 'agree':
+            curs.execute("select sub from rd where title = ? and agree = 'O' order by sub asc", [name])
+            
+            sub = load_lang('agreement') + ''
+        else:
+            curs.execute("select sub from rd where title = ? order by date desc", [name])
+            
+            sub = load_lang('discussion_list')
+            
+            menu = [['w/' + url_pas(name), load_lang('document')]]
+            
+            plus =  '''
+                    <a href="/topic/''' + url_pas(name) + '''/close">(''' + load_lang('close') + ''')</a> <a href="/topic/''' + url_pas(name) + '''/agree">(''' + load_lang('agreement') + ''')</a>
+                    <hr class=\"main_hr\">
+                    <input placeholder="''' + load_lang('discussion_name') + '''" name="topic" type="text">
+                    <hr class=\"main_hr\">
+                    <button type="submit">''' + load_lang('go') + '''</button>
+                    '''
+
+        for data in curs.fetchall():
+            curs.execute("select data, date, ip, block from topic where title = ? and sub = ? and id = '1'", [name, data[0]])
+            if curs.fetchall():                
+                it_p = 0
+                
+                if sub == load_lang('discussion_list'):
+                    curs.execute("select title from rd where title = ? and sub = ? and stop = 'O' order by sub asc", [name, data[0]])
+                    if curs.fetchall():
+                        it_p = 1
+                
+                if it_p != 1:
+                    div += '<h2><a href="/topic/' + url_pas(name) + '/sub/' + url_pas(data[0]) + '">' + data[0] + '</a></h2>'
+
+        if div == '':
+            plus = re.sub('^<br>', '', plus)
+        
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [name, wiki_set(), custom(), other2([' (' + sub + ')', 0])],
+            data =  '<form method="post">' + div + plus + '</form>',
+            menu = menu
+        ))

+ 34 - 0
route/count_edit.py

@@ -0,0 +1,34 @@
+from .tool.func import *
+
+def count_edit_2(conn, name):
+    curs = conn.cursor()
+
+    if name == None:
+        that = ip_check()
+    else:
+        that = name
+
+    curs.execute("select count(title) from history where ip = ?", [that])
+    count = curs.fetchall()
+    if count:
+        data = count[0][0]
+    else:
+        data = 0
+
+    curs.execute("select count(title) from topic where ip = ?", [that])
+    count = curs.fetchall()
+    if count:
+        t_data = count[0][0]
+    else:
+        t_data = 0
+
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [load_lang('count'), wiki_set(), custom(), other2([0, 0])],
+        data =  '''
+                <ul>
+                    <li><a href="/record/''' + url_pas(that) + '''">''' + load_lang('edit_record') + '''</a> : ''' + str(data) + '''</li>
+                    <li><a href="/topic_record/''' + url_pas(that) + '''">''' + load_lang('discussion_record') + '''</a> : ''' + str(t_data) + '''</a></li>
+                </ul>
+                ''',
+        menu = [['user', load_lang('return')]]
+    ))

+ 51 - 0
route/custom_head_view.py

@@ -0,0 +1,51 @@
+from .tool.func import *
+
+def custom_head_view_2(conn):
+    curs = conn.cursor()
+
+    ip = ip_check()
+
+    if flask.request.method == 'POST':
+        if custom()[2] != 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)'])
+            else:
+                curs.execute("insert into custom (user, css) values (?, ?)", [ip + ' (head)', flask.request.form.get('content', None)])
+            
+            conn.commit()
+
+        flask.session['head'] = flask.request.form.get('content', None)
+
+        return redirect('/user')
+    else:
+        if custom()[2] != 0:
+            start = ''
+
+            curs.execute("select css from custom where user = ?", [ip + ' (head)'])
+            head_data = curs.fetchall()
+            if head_data:
+                data = head_data[0][0]
+            else:
+                data = ''
+        else:
+            start = '<span>' + load_lang('user_head_warring') + '</span><hr class=\"main_hr\">'
+            
+            if 'head' in flask.session:
+                data = flask.session['head']
+            else:
+                data = ''
+
+        start += '<span>&lt;style&gt;CSS&lt;/style&gt;<br>&lt;script&gt;JS&lt;/script&gt;</span><hr class=\"main_hr\">'
+
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [load_lang(data = 'user_head', safe = 1), wiki_set(), custom(), other2([0, 0])],
+            data =  start + '''
+                    <form method="post">
+                        <textarea rows="25" cols="100" name="content">''' + data + '''</textarea>
+                        <hr class=\"main_hr\">
+                        <button id="save" type="submit">''' + load_lang('save') + '''</button>
+                    </form>
+                    ''',
+            menu = [['user', load_lang('return')]]
+        ))

+ 61 - 0
route/deep_search.py

@@ -0,0 +1,61 @@
+from .tool.func import *
+
+def deep_search_2(conn, name):
+    curs = conn.cursor()
+
+    if name == '':
+        return redirect()
+
+    num = int(number_check(flask.request.args.get('num', '1')))
+    if num * 50 > 0:
+        sql_num = num * 50 - 50
+    else:
+        sql_num = 0
+
+    div = '<ul>'
+    
+    div_plus = ''
+    test = ''
+    
+    curs.execute("select title from data where title = ?", [name])
+    if curs.fetchall():
+        link_id = ''
+    else:
+        link_id = 'id="not_thing"'
+    
+    div =   '''
+            <ul>
+                <li>
+                    <a ''' + link_id + ' href="/w/' + url_pas(name) + '">' + name + '''</a>
+                </li>
+            </ul>
+            <hr class=\"main_hr\">
+            <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'",
+        ['%' + name + '%', '%' + name + '%', '%' + name + '%', '%' + name + '%', str(sql_num)]
+    )
+    all_list = curs.fetchall()
+    if all_list:
+        test = all_list[0][1]
+        
+        for data in all_list:
+            if data[1] != test:
+                div_plus += '</ul><hr class=\"main_hr\"><ul>'
+                
+                test = data[1]
+
+            div_plus += '<li><a href="/w/' + url_pas(data[0]) + '">' + data[0] + '</a> (' + data[1] + ')</li>'
+
+    div += div_plus + '</ul>'
+    div += next_fix('/search/' + url_pas(name) + '?num=', num, all_list)
+
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [name, wiki_set(), custom(), other2([' (' + load_lang('search') + ')', 0])],
+        data = div,
+        menu = 0
+    ))

+ 9 - 0
route/del_alarm.py

@@ -0,0 +1,9 @@
+from .tool.func import *
+
+def del_alarm_2(conn):
+    curs = conn.cursor()
+    
+    curs.execute("delete from alarm where name = ?", [ip_check()])
+    conn.commit()
+
+    return redirect('/alarm')

+ 20 - 0
route/del_inter.py

@@ -0,0 +1,20 @@
+from .tool.func import *
+
+def del_inter_2(conn, tools, name):
+    curs = conn.cursor()
+    
+    if admin_check(None, tools) == 1:
+        if tools == 'del_inter_wiki':
+            curs.execute("delete from inter where title = ?", [name])
+        elif tools == 'del_edit_filter':
+            curs.execute("delete from filter where name = ?", [name])
+        elif tools == 'del_name_filter':
+            curs.execute("delete from html_filter where html = ? and kind = 'name'", [name])
+        else:
+            curs.execute("delete from html_filter where html = ? and kind = 'email'", [name])
+        
+        conn.commit()
+
+        return redirect('/' + re.sub('^del_', '', tools))
+    else:
+        return re_error('/error/3')

+ 57 - 0
route/delete.py

@@ -0,0 +1,57 @@
+from .tool.func import *
+
+def delete_2(conn, name):
+    curs = conn.cursor()
+
+    ip = ip_check()
+    if acl_check(name) == 1:
+        return re_error('/ban')
+    
+    if flask.request.method == 'POST':
+        if captcha_post(flask.request.form.get('g-recaptcha-response', '')) == 1:
+            return re_error('/error/13')
+        else:
+            captcha_post('', 0)
+
+        curs.execute("select data from data where title = ?", [name])
+        data = curs.fetchall()
+        if data:
+            today = get_time()
+            leng = '-' + str(len(data[0][0]))
+            
+            history_plus(
+                name, 
+                '', 
+                today, 
+                ip, 
+                flask.request.form.get('send', None) + ' (delete)', 
+                leng
+            )
+            
+            curs.execute("select title, link from back where title = ? and not type = 'cat' and not type = 'no'", [name])
+            for data in curs.fetchall():
+                curs.execute("insert into back (title, link, type) values (?, ?, 'no')", [data[0], data[1]])
+            
+            curs.execute("delete from back where link = ?", [name])
+            curs.execute("delete from data where title = ?", [name])
+            conn.commit()
+            
+        return redirect('/w/' + url_pas(name))
+    else:
+        curs.execute("select title from data where title = ?", [name])
+        if not curs.fetchall():
+            return redirect('/w/' + url_pas(name))
+
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [name, wiki_set(), custom(), other2([' (' + load_lang('delete') + ')', 0])],
+            data =  '''
+                    <form method="post">
+                        ''' + ip_warring() + '''
+                        <input placeholder="''' + load_lang('why') + '''" name="send" type="text">
+                        <hr class=\"main_hr\">
+                        ''' + captcha_get() + '''
+                        <button type="submit">''' + load_lang('delete') + '''</button>
+                    </form>
+                    ''',
+            menu = [['w/' + url_pas(name), load_lang('return')]]
+        ))     

+ 30 - 0
route/diff_data.py

@@ -0,0 +1,30 @@
+from .tool.func import *
+
+def diff_data_2(conn, name):
+    curs = conn.cursor()
+
+    first = flask.request.args.get('first', '1')
+    second = flask.request.args.get('second', '1')
+
+    curs.execute("select data from history where id = ? and title = ?", [first, name])
+    first_raw_data = curs.fetchall()
+    if first_raw_data:
+        curs.execute("select data from history where id = ? and title = ?", [second, name])
+        second_raw_data = curs.fetchall()
+        if second_raw_data:
+            first_data = html.escape(first_raw_data[0][0])            
+            second_data = html.escape(second_raw_data[0][0])
+
+            if first == second:
+                result = '-'
+            else:            
+                diff_data = difflib.SequenceMatcher(None, first_data, second_data)
+                result = re.sub('\r', '', diff(diff_data))
+            
+            return easy_minify(flask.render_template(skin_check(), 
+                imp = [name, wiki_set(), custom(), other2([' (' + load_lang('compare') + ')', 0])],
+                data = '<pre>' + result + '</pre>',
+                menu = [['history/' + url_pas(name), load_lang('return')]]
+            ))
+
+    return redirect('/history/' + url_pas(name))

+ 18 - 0
route/down.py

@@ -0,0 +1,18 @@
+from .tool.func import *
+
+def down_2(conn, name):
+    curs = conn.cursor()
+
+    div = '<ul>'
+
+    curs.execute("select title from data where title like ?", ['%' + name + '/%'])
+    for data in curs.fetchall():
+        div += '<li><a href="/w/' + url_pas(data[0]) + '">' + data[0] + '</a></li>'
+        
+    div += '</ul>'
+    
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [name, wiki_set(), custom(), other2([' (' + load_lang('sub') + ')', 0])],
+        data = div,
+        menu = [['w/' + url_pas(name), load_lang('return')]]
+    ))

+ 118 - 0
route/edit.py

@@ -0,0 +1,118 @@
+from .tool.func import *
+
+def edit_2(conn, name):
+    curs = conn.cursor()
+
+    ip = ip_check()
+    if acl_check(name) == 1:
+        return re_error('/ban')
+    
+    if flask.request.method == 'POST':
+        if captcha_post(flask.request.form.get('g-recaptcha-response', '')) == 1:
+            return re_error('/error/13')
+        else:
+            captcha_post('', 0)
+            
+        if len(flask.request.form.get('send', None)) > 500:
+            return re_error('/error/15')
+
+        if flask.request.form.get('otent', None) == flask.request.form.get('content', None):
+            return redirect('/w/' + url_pas(name))
+            
+        if edit_filter_do(flask.request.form.get('content', '')) == 1:
+            return re_error('/error/21')
+
+        today = get_time()
+        content = savemark(flask.request.form.get('content', None))
+        
+        curs.execute("select data from data where title = ?", [name])
+        old = curs.fetchall()
+        if old:
+            leng = leng_check(len(flask.request.form.get('otent', None)), len(content))
+            
+            if flask.request.args.get('section', None):
+                content = old[0][0].replace(flask.request.form.get('otent', None), content)
+                
+            curs.execute("update data set data = ? where title = ?", [content, name])
+        else:
+            leng = '+' + str(len(content))
+            
+            curs.execute("insert into data (title, data) values (?, ?)", [name, content])
+
+        curs.execute("select user from scan where title = ?", [name])
+        for _ in curs.fetchall():
+            curs.execute("insert into alarm (name, data, date) values (?, ?, ?)", [ip, ip + ' - <a href="/w/' + url_pas(name) + '">' + name + '</a> (Edit)', today])
+
+        history_plus(
+            name,
+            content,
+            today,
+            ip,
+            flask.request.form.get('send', None),
+            leng
+        )
+        
+        curs.execute("delete from back where link = ?", [name])
+        curs.execute("delete from back where title = ? and type = 'no'", [name])
+        
+        render_set(
+            title = name,
+            data = content,
+            num = 1
+        )
+        
+        conn.commit()
+        
+        return redirect('/w/' + url_pas(name))
+    else:            
+        curs.execute("select data from data where title = ?", [name])
+        new = curs.fetchall()
+        if new:
+            if flask.request.args.get('section', None):
+                test_data = '\n' + re.sub('\r\n', '\n', new[0][0]) + '\n'   
+                
+                section_data = re.findall('((?:={1,6}) ?(?:(?:(?!={1,6}\n).)+) ?={1,6}\n(?:(?:(?!(?:={1,6}) ?(?:(?:(?!={1,6}\n).)+) ?={1,6}\n).)*\n*)*)', test_data)
+                data = section_data[int(flask.request.args.get('section', None)) - 1]
+            else:
+                data = new[0][0]
+        else:
+            data = ''
+            
+        data_old = data
+        
+        if not flask.request.args.get('section', None):
+            get_name =  '''
+                        <a href="/manager/15?plus=''' + url_pas(name) + '">(' + load_lang('load') + ')</a> <a href="/edit_filter">(' + load_lang('edit_filter_rule') + ''')</a>
+                        <hr class=\"main_hr\">
+                        '''
+            action = ''
+        else:
+            get_name = ''
+            action = '?section=' + flask.request.args.get('section', None)
+            
+        if flask.request.args.get('plus', None):
+            curs.execute("select data from data where title = ?", [flask.request.args.get('plus', None)])
+            get_data = curs.fetchall()
+            if get_data:
+                data = get_data[0][0]
+                get_name = ''
+
+        js_data = edit_help_button()
+
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [name, wiki_set(), custom(), other2([' (' + load_lang('edit') + ')', 0])],
+            data =  get_name + js_data[0] + '''
+                    <form method="post" action="/edit/''' + url_pas(name) + action + '''">
+                        ''' + js_data[1] + '''
+                        <textarea id="content" rows="25" name="content">''' + html.escape(re.sub('\n$', '', data)) + '''</textarea>
+                        <textarea style="display: none;" name="otent">''' + html.escape(re.sub('\n$', '', data_old)) + '''</textarea>
+                        <hr class=\"main_hr\">
+                        <input placeholder="''' + load_lang('why') + '''" name="send" type="text">
+                        <hr class=\"main_hr\">
+                        ''' + captcha_get() + ip_warring() + '''
+                        <button id="save" type="submit">''' + load_lang('save') + '''</button>
+                        <button id="preview" type="submit" formaction="/preview/''' + url_pas(name) + action + '">' + load_lang('preview') + '''</button>
+                    </form>
+                    ''',
+            menu = [['w/' + url_pas(name), load_lang('return')], ['delete/' + url_pas(name), load_lang('delete')], ['move/' + url_pas(name), load_lang('move')]]
+        ))

+ 22 - 0
route/give_log.py

@@ -0,0 +1,22 @@
+from .tool.func import *
+
+def give_log_2(conn):
+    curs = conn.cursor()
+
+    list_data = '<ul>'
+    back = ''
+
+    curs.execute("select distinct name from alist order by name asc")
+    for data in curs.fetchall():                      
+        if back != data[0]:
+            back = data[0]
+
+        list_data += '<li><a href="/admin_plus/' + url_pas(data[0]) + '">' + data[0] + '</a></li>'
+    
+    list_data += '</ul><hr class=\"main_hr\"><a href="/manager/8">(' + load_lang('add') + ')</a>'
+
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [load_lang('admin_group_list'), wiki_set(), custom(), other2([0, 0])],
+        data = list_data,
+        menu = [['other', load_lang('return')]]
+    ))    

+ 17 - 0
route/history_hidden.py

@@ -0,0 +1,17 @@
+from .tool.func import *
+
+def history_hidden_2(conn, name):
+    curs = conn.cursor()
+
+    num = number_check(flask.request.args.get('num', '1'))
+
+    if admin_check(6, 'history_hidden (' + name + '#' + num + ')') == 1:
+        curs.execute("select title from history where title = ? and id = ? and hide = 'O'", [name, num])
+        if curs.fetchall():
+            curs.execute("update history set hide = '' where title = ? and id = ?", [name, num])
+        else:
+            curs.execute("update history set hide = 'O' where title = ? and id = ?", [name, num])
+            
+        conn.commit()
+    
+    return redirect('/history/' + url_pas(name))

+ 11 - 0
route/image_view.py

@@ -0,0 +1,11 @@
+from .tool.func import *
+
+APPVAR = json.loads(open('data/app_variables.json', encoding='utf-8').read())
+
+def image_view_2(conn, name):
+    curs = conn.cursor()
+    
+    if os.path.exists(os.path.join(APPVAR['PATH_DATA_IMAGES'], name)):
+        return flask.send_from_directory('.'+APPVAR['PATH_DATA_IMAGES'], name)
+    else:
+        return redirect()

+ 60 - 0
route/indexing.py

@@ -0,0 +1,60 @@
+from .tool.func import *
+
+def indexing_2(conn):
+    curs = conn.cursor()
+
+    if admin_check() != 1:
+        return re_error('/error/3')
+
+    if flask.request.method == 'POST':
+        admin_check(None, 'indexing')
+
+        curs.execute("select name from sqlite_master where type = 'index'")
+        data = curs.fetchall()
+        if data:
+            for delete_index in data:
+                print('Delete : ' + delete_index[0])
+
+                sql = 'drop index if exists ' + delete_index[0]
+                
+                try:
+                    curs.execute(sql)
+                except:
+                    pass
+        else:
+            curs.execute("select name from sqlite_master where type in ('table', 'view') and name not like 'sqlite_%' union all select name from sqlite_temp_master where type in ('table', 'view') order by 1;")
+            for table in curs.fetchall():            
+                curs.execute('select sql from sqlite_master where name = ?', [table[0]])
+                cul = curs.fetchall()
+                
+                r_cul = re.findall('(?:([^ (]*) text)', str(cul[0]))
+                
+                for n_cul in r_cul:
+                    print('Create : index_' + table[0] + '_' + n_cul)
+
+                    sql = 'create index index_' + table[0] + '_' + n_cul + ' on ' + table[0] + '(' + n_cul + ')'
+                    try:
+                        curs.execute(sql)
+                    except:
+                        pass
+
+        conn.commit()
+        
+        return redirect()  
+    else:
+        curs.execute("select name from sqlite_master where type = 'index'")
+        data = curs.fetchall()
+        if data:
+            b_data = load_lang('delete')
+        else:
+            b_data = load_lang('create')
+
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [load_lang('indexing'), wiki_set(), custom(), other2([0, 0])],
+            data =  '''
+                    <form method="post">
+                        <button type="submit">''' + b_data + '''</button>
+                    </form>
+                    ''',
+            menu = [['manager', load_lang('return')]]
+        ))   

+ 75 - 0
route/inter_wiki.py

@@ -0,0 +1,75 @@
+from .tool.func import *
+
+def inter_wiki_2(conn, tools):
+    curs = conn.cursor()
+    
+    div = ''
+    admin = admin_check()
+
+    if tools == 'inter_wiki':
+        del_link = 'del_inter_wiki'
+        plus_link = 'plus_inter_wiki'
+        title = load_lang('interwiki_list')
+        div = ''
+
+        curs.execute('select title, link from inter')
+    elif tools == 'email_filter':
+        del_link = 'del_email_filter'
+        plus_link = 'plus_email_filter'
+        title = load_lang('email_filter_list')
+        div =   '''
+                <ul>
+                    <li>gmail.com</li>
+                    <li>naver.com</li>
+                    <li>daum.net</li>
+                    <li>hanmail.net</li>
+                    <li>hanmail2.net</li>
+                </ul>
+                '''
+
+        curs.execute("select html from html_filter where kind = 'email'")
+    elif tools == 'name_filter':
+        del_link = 'del_name_filter'
+        plus_link = 'plus_name_filter'
+        title = load_lang('id_filter_list')
+        div = ''
+
+        curs.execute("select html from html_filter where kind = 'name'")
+    else:
+        del_link = 'del_edit_filter'
+        plus_link = 'manager/9'
+        title = load_lang('edit_filter_list')
+        div = ''
+
+        curs.execute("select name from filter")
+
+    db_data = curs.fetchall()
+    if db_data:
+        div += '<ul>'
+
+        for data in db_data:
+            if tools == 'inter_wiki':
+                div += '<li>' + data[0] + ' : <a id="out_link" href="' + data[1] + '">' + data[1] + '</a>'
+            elif tools == 'edit_filter':
+                div += '<li><a href="/plus_edit_filter/' + url_pas(data[0]) + '">' + data[0] + '</a>'
+            else:
+                div += '<li>' + data[0]
+
+            if admin == 1:
+                div += ' <a href="/' + del_link + '/' + url_pas(data[0]) + '">(' + load_lang('delete') + ')</a>'
+
+            div += '</li>'
+
+        div += '</ul>'
+
+        if admin == 1:
+            div += '<hr class=\"main_hr\"><a href="/' + plus_link + '">(' + load_lang('add') + ')</a>'
+    else:
+        if admin == 1:
+            div += '<a href="/' + plus_link + '">(' + load_lang('add') + ')</a>'
+
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [title, wiki_set(), custom(), other2([0, 0])],
+        data = div,
+        menu = [['other', load_lang('return')]]
+    ))

+ 91 - 0
route/login.py

@@ -0,0 +1,91 @@
+from .tool.func import *
+
+def login_2(conn):
+    curs = conn.cursor()
+
+    if custom()[2] != 0:
+        return redirect('/user')
+    
+    if ban_check(tool = 'login') == 1:
+        return re_error('/ban')
+        
+    if flask.request.method == 'POST':        
+        if captcha_post(flask.request.form.get('g-recaptcha-response', '')) == 1:
+            return re_error('/error/13')
+        else:
+            captcha_post('', 0)
+
+        ip = ip_check()
+        agent = flask.request.headers.get('User-Agent')
+
+        curs.execute("select pw, encode from user where id = ?", [flask.request.form.get('id', None)])
+        user = curs.fetchall()
+        if not user:
+            return re_error('/error/2')
+
+        pw_check_d = pw_check(
+            flask.request.form.get('pw', ''), 
+            user[0][0],
+            user[0][1],
+            flask.request.form.get('id', None)
+        )
+        if pw_check_d != 1:
+            return re_error('/error/10')
+
+        flask.session['state'] = 1
+        flask.session['id'] = flask.request.form.get('id', None)
+        
+        curs.execute("select css from custom where user = ?", [flask.request.form.get('id', None)])
+        css_data = curs.fetchall()
+        if css_data:
+            flask.session['head'] = css_data[0][0]
+        else:
+            flask.session['head'] = ''
+
+        curs.execute("insert into ua_d (name, ip, ua, today, sub) values (?, ?, ?, ?, '')", [flask.request.form.get('id', None), ip_check(1), agent, get_time()])
+
+        conn.commit()
+        
+        return redirect('/user')  
+    else:
+        oauth_content = '<link rel="stylesheet" href="/views/main_css/oauth.css"><div class="oauth-wrapper"><ul class="oauth-list">'
+        oauth_supported = load_oauth('_README')['support']
+        for i in range(len(oauth_supported)):
+            oauth_data = load_oauth(oauth_supported[i])
+            if oauth_data['client_id'] != '' and oauth_data['client_secret'] != '':
+                oauth_content +=    '''
+                                    <li>
+                                        <a href="/oauth/{}/init">
+                                            <div class="oauth-btn oauth-btn-{}">
+                                                <div class="oauth-btn-logo oauth-btn-{}"></div>
+                                                {}
+                                            </div>
+                                        </a>
+                                    </li>
+                                    '''.format(
+                                        oauth_supported[i], 
+                                        oauth_supported[i], 
+                                        oauth_supported[i], 
+                                        load_lang('oauth_signin_' + oauth_supported[i])
+                                    )
+        
+        oauth_content += '</ul></div>'
+        
+        return easy_minify(flask.render_template(skin_check(),    
+            imp = [load_lang('login'), wiki_set(), custom(), other2([0, 0])],
+            data =  '''
+                    <form method="post">
+                        <input placeholder="''' + load_lang('id') + '''" name="id" type="text">
+                        <hr class=\"main_hr\">
+                        <input placeholder="''' + load_lang('password') + '''" name="pw" type="password">
+                        <hr class=\"main_hr\">
+                        ''' + captcha_get() + '''
+                        <button type="submit">''' + load_lang('login') + '''</button>
+                        <hr class=\"main_hr\">
+                        ''' + oauth_content + '''
+                        <hr class=\"main_hr\">
+                        <span>''' + load_lang('http_warring') + '''</span>
+                    </form>
+                    ''',
+            menu = [['user', load_lang('return')]]
+        ))

+ 187 - 0
route/login_oauth.py

@@ -0,0 +1,187 @@
+from .tool.func import *
+
+def login_oauth_2(conn, platform, func):
+    curs = conn.cursor()
+
+    publish_url = load_oauth('publish_url')
+    oauth_data = load_oauth(platform)
+    api_url = {}
+    data = {
+        'client_id' : oauth_data['client_id'],
+        'client_secret' : oauth_data['client_secret'],
+        'redirect_uri' : publish_url + '/oauth/' + platform + '/callback',
+        'state' : 'RAMDOMVALUE'
+    }
+
+    if platform == 'discord':
+        api_url['redirect'] = 'https://discordapp.com/api/oauth2/authorize'
+        api_url['token'] = 'https://discordapp.com/api/oauth2/token'
+        api_url['profile'] = 'https://discordapp.com/api/users/@me'
+    elif platform == 'naver':
+        api_url['redirect'] = 'https://nid.naver.com/oauth2.0/authorize'
+        api_url['token'] = 'https://nid.naver.com/oauth2.0/token'
+        api_url['profile'] = 'https://openapi.naver.com/v1/nid/me'
+    elif platform == 'facebook':
+        api_url['redirect'] = 'https://www.facebook.com/v3.1/dialog/oauth'
+        api_url['token'] = 'https://graph.facebook.com/v3.1/oauth/access_token'
+        api_url['profile'] = 'https://graph.facebook.com/me'
+
+    if func == 'init':
+        if oauth_data['client_id'] == '' or oauth_data['client_secret'] == '':
+            return easy_minify(flask.render_template(skin_check(), 
+                imp = [load_lang('error'), wiki_set(), custom(), other2([0, 0])], 
+                data = load_lang('oauth_disabled'), 
+                menu = [['user', load_lang('return')]]
+            ))
+        elif publish_url == 'https://':
+            return easy_minify(flask.render_template(skin_check(), 
+                imp = [load_lang('error'), wiki_set(), custom(), other2([0, 0])], 
+                data = load_lang('oauth_setting_not_found'), 
+                menu = [['user', load_lang('return')]]
+            ))
+
+        referrer_re = re.compile(r'(?P<host>^(https?):\/\/([^\/]+))\/(?P<refer>[^\/?]+)')
+        if flask.request.referrer != None:
+            referrer = referrer_re.search(flask.request.referrer)
+            if referrer.group('host') != load_oauth('publish_url'):
+                return redirect()
+            else:
+                flask.session['referrer'] = referrer.group('refer')
+        else:
+            return redirect()
+
+        flask.session['refer'] = flask.request.referrer
+
+        if platform == 'discord':
+            return redirect(api_url['redirect'] + '?client_id={}&redirect_uri={}&response_type=code&scope=identify'.format(
+                data['client_id'], 
+                data['redirect_uri']
+            ))
+        elif platform == 'naver':
+            return redirect(api_url['redirect'] + '?response_type=code&client_id={}&redirect_uri={}&state={}'.format(
+                data['client_id'], 
+                data['redirect_uri'], 
+                data['state']
+            ))
+        elif platform == 'facebook':
+            return redirect(api_url['redirect'] + '?client_id={}&redirect_uri={}&state={}'.format(
+                data['client_id'], 
+                data['redirect_uri'], 
+                data['state']
+            ))
+
+    elif func == 'callback':
+        code = flask.request.args.get('code')
+        state = flask.request.args.get('state')
+
+        if code == None:
+            return easy_minify(flask.render_template(skin_check(),
+                imp = [load_lang('inter_error'), wiki_set(), custom(), other2([0, 0])],
+                data = '<h2>ie_wrong_callback</h2>' + load_lang('ie_wrong_callback'),
+                menu = [['user', load_lang('return')]]
+            ))
+
+        if platform == 'discord':
+            data = {
+                'client_id'     : data['client_id'],
+                'client_secret' : data['client_secret'],
+                'grant_type'    : 'authorization_code',
+                'redirect_uri'  : data['redirect_uri'],
+                'scope'         : 'identify',
+                'code'          : code
+            }
+            headers = {
+                'Content-Type': 'application/x-www-form-urlencoded',
+                'User-Agent': 'Mozilla/5.0'
+            }
+            token_exchange = urllib.request.Request(
+                'https://discordapp.com/api/oauth2/token',
+                data = bytes(urllib.parse.urlencode(data).encode()),
+                headers = headers
+            )
+            token_result = urllib.request.urlopen(token_exchange).read()
+            token_json = json.loads(token_result)
+
+            headers = {
+                'User-Agent'    : 'Mozilla/5.0',
+                'Authorization' : 'Bearer ' + token_json['access_token']
+            }
+            profile_exchange = urllib.request.Request(
+                'https://discordapp.com/api/users/@me',
+                headers = headers
+            )
+            profile_result =  urllib.request.urlopen(profile_exchange).read().decode('utf-8')
+            profile_result_json = json.loads(profile_result)
+            stand_json = {
+                'id'        : profile_result_json['id'], 
+                'name'      : profile_result_json['username'] + '#' + profile_result_json['discriminator'],
+                'picture'   : profile_result_json['avatar']
+            }
+        elif platform == 'naver':
+            token_access = api_url['token'] + '?grant_type=authorization_code&client_id={}&client_secret={}&code={}&state={}'.format(
+                data['client_id'], 
+                data['client_secret'], 
+                code, 
+                state
+            )
+            token_result = urllib.request.urlopen(token_access).read().decode('utf-8')
+            token_result_json = json.loads(token_result)
+
+            headers = {
+                'Authorization': 'Bearer {}'.format(token_result_json['access_token'])
+            }
+
+            profile_access = urllib.request.Request(api_url['profile'], headers = headers)
+            profile_result = urllib.request.urlopen(profile_access).read().decode('utf-8')
+            profile_result_json = json.loads(profile_result)
+
+            stand_json = {
+                'id'        : profile_result_json['response']['id'],
+                'name'      : profile_result_json['response']['name'],
+                'picture'   : profile_result_json['response']['profile_image']
+            }
+        elif platform == 'facebook':
+            token_access = api_url['token'] + '?client_id={}&redirect_uri={}&client_secret={}&code={}'.format(
+                data['client_id'], 
+                data['redirect_uri'], 
+                data['client_secret'], 
+                code
+            )
+            token_result = urllib.request.urlopen(token_access).read().decode('utf-8')
+            token_result_json = json.loads(token_result)
+
+            profile_access = api_url['profile'] + '?fields=id,name,picture&access_token={}'.format(token_result_json['access_token'])
+            profile_result = urllib.request.urlopen(profile_access).read().decode('utf-8')
+            profile_result_json = json.loads(profile_result)
+
+            stand_json = {
+                'id': profile_result_json['id'], 
+                'name': profile_result_json['name'], 
+                'picture': profile_result_json['picture']['data']['url']
+            }
+        
+        if flask.session['referrer'][0:6] == 'change':
+            curs.execute('select * from oauth_conn where wiki_id = ? and provider = ?', [flask.session['id'], platform])
+            oauth_result = curs.fetchall()
+            if len(oauth_result) == 0:
+                curs.execute('insert into oauth_conn (provider, wiki_id, sns_id, name, picture) values(?, ?, ?, ?, ?)', [
+                    platform, 
+                    flask.session['id'], 
+                    stand_json['id'], 
+                    stand_json['name'], 
+                    stand_json['picture']
+                ])
+            else:
+                curs.execute('update oauth_conn set name = ? picture = ? where wiki_id = ?', [stand_json['name'], stand_json['pricture'], flask.session['id']])
+
+            conn.commit()
+        elif flask.session['referrer'][0:5] == 'login':
+            curs.execute('select * from oauth_conn where provider = ? and sns_id = ?', [platform, stand_json['id']])
+            curs_result = curs.fetchall()
+            if len(curs_result) == 0:
+                return re_error('/error/2')
+            else:
+                flask.session['state'] = 1
+                flask.session['id'] = curs_result[0][2]
+        
+        return redirect(flask.session['refer'])

+ 83 - 0
route/manager.py

@@ -0,0 +1,83 @@
+from .tool.func import *
+
+def manager_2(conn, num):
+    curs = conn.cursor()
+    
+    title_list = {
+        0 : [load_lang('document_name'), 'acl'], 
+        1 : [0, 'check'], 
+        2 : [0, 'ban'], 
+        3 : [0, 'admin'], 
+        4 : [0, 'record'], 
+        5 : [0, 'topic_record'], 
+        6 : [load_lang('name'), 'admin_plus'], 
+        7 : [load_lang('name'), 'plus_edit_filter'], 
+        8 : [load_lang('document_name'), 'search'], 
+        9 : [0, 'block_user'], 
+        10 : [0, 'block_admin'], 
+        11 : [load_lang('document_name'), 'watch_list'], 
+        12 : [load_lang('compare_target'), 'check'], 
+        13 : [load_lang('document_name'), 'edit']
+    }
+    
+    if num == 1:
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [load_lang('admin_tool'), wiki_set(), custom(), other2([0, 0])],
+            data =  '''
+                    <h2>''' + load_lang('admin') + '''</h2>
+                    <ul>
+                        <li><a href="/manager/2">''' + load_lang('acl_document_list') + '''</a></li>
+                        <li><a href="/manager/3">''' + load_lang('check_user') + '''</a></li>
+                        <li><a href="/manager/4">''' + load_lang('ban') + '''</a></li>
+                        <li><a href="/manager/5">''' + load_lang('authorize') + '''</a></li>
+                        <li><a href="/edit_filter">''' + load_lang('edit_filter_list') + '''</a></li>
+                    </ul>
+                    <br>
+                    <h2>''' + load_lang('owner') + '''</h2>
+                    <ul>
+                        <li><a href="/manager/8">''' + load_lang('admin_group_add') + '''</a></li>
+                        <li><a href="/setting">''' + load_lang('setting') + '''</a></li>
+                    </ul>
+                    <h3>''' + load_lang('filter') + '''</h3>
+                    <ul>
+                        <li><a href="/inter_wiki">''' + load_lang('interwiki_list') + '''</a></li>
+                        <li><a href="/email_filter">''' + load_lang('email_filter_list') + '''</a></li>
+                        <li><a href="/name_filter">''' + load_lang('id_filter_list') + '''</a></li>
+                    </ul>
+                    <br>
+                    <h2>''' + load_lang('server') + '''</h2>
+                    <ul>
+                        <li><a href="/indexing">''' + load_lang('indexing') + '''</a></li>
+                        <li><a href="/restart">''' + load_lang('wiki_restart') + '''</a></li>
+                        <li><a href="/update">''' + load_lang('update') + '''</a></li>
+                        <li><a href="/oauth_setting">''' + load_lang('oauth_setting') + '''</a></li>
+                        <li><a href="/adsense_setting">''' + load_lang('adsense_setting') + '''</a></li>
+                    </ul>
+                    ''',
+            menu = [['other', load_lang('return')]]
+        ))
+    elif not num - 1 > len(title_list):
+        if flask.request.method == 'POST':
+            if flask.request.args.get('plus', None):
+                return redirect('/' + title_list[(num - 2)][1] + '/' + url_pas(flask.request.args.get('plus', None)) + '?plus=' + flask.request.form.get('name', None))
+            else:
+                return redirect('/' + title_list[(num - 2)][1] + '/' + url_pas(flask.request.form.get('name', None)))
+        else:
+            if title_list[(num - 2)][0] == 0:
+                placeholder = load_lang('user_name')
+            else:
+                placeholder = title_list[(num - 2)][0]
+
+            return easy_minify(flask.render_template(skin_check(), 
+                imp = ['Redirect', wiki_set(), custom(), other2([0, 0])],
+                data =  '''
+                        <form method="post">
+                            <input placeholder="''' + placeholder + '''" name="name" type="text">
+                            <hr class=\"main_hr\">
+                            <button type="submit">''' + load_lang('go') + '''</button>
+                        </form>
+                        ''',
+                menu = [['manager', load_lang('return')]]
+            ))
+    else:
+        return redirect()

+ 103 - 0
route/move.py

@@ -0,0 +1,103 @@
+from .tool.func import *
+
+def move_2(conn, name):
+    curs = conn.cursor()
+
+    if acl_check(name) == 1:
+        return re_error('/ban')
+
+    if flask.request.method == 'POST':
+        if captcha_post(flask.request.form.get('g-recaptcha-response', '')) == 1:
+            return re_error('/error/13')
+        else:
+            captcha_post('', 0)
+
+        curs.execute("select title from history where title = ?", [flask.request.form.get('title', None)])
+        if curs.fetchall():
+            if admin_check(None, 'merge documents') == 1:
+                curs.execute("select data from data where title = ?", [flask.request.form.get('title', None)])
+                data = curs.fetchall()
+                if data:            
+                    curs.execute("delete from data where title = ?", [flask.request.form.get('title', None)])
+                    curs.execute("delete from back where link = ?", [flask.request.form.get('title', None)])
+                
+                curs.execute("select data from data where title = ?", [name])
+                data = curs.fetchall()
+                if data:            
+                    curs.execute("update data set title = ? where title = ?", [flask.request.form.get('title', None), name])
+                    curs.execute("update back set link = ? where link = ?", [flask.request.form.get('title', None), name])
+                    
+                    data_in = data[0][0]
+                else:
+                    data_in = ''
+
+                history_plus(
+                    name, 
+                    data_in, 
+                    get_time(), 
+                    ip_check(), 
+                    flask.request.form.get('send', None) + ' (marge <a>' + name + '</a> - <a>' + flask.request.form.get('title', None) + '</a> move)', 
+                    '0'
+                )
+
+                curs.execute("update back set type = 'no' where title = ? and not type = 'cat' and not type = 'no'", [name])
+                curs.execute("delete from back where title = ? and not type = 'cat' and type = 'no'", [flask.request.form.get('title', None)])
+
+                curs.execute("select id from history where title = ? order by id + 0 desc limit 1", [flask.request.form.get('title', None)])
+                data = curs.fetchall()
+                
+                num = data[0][0]
+
+                curs.execute("select id from history where title = ? order by id + 0 asc", [name])
+                data = curs.fetchall()
+                for move in data:
+                    curs.execute("update history set title = ?, id = ? where title = ? and id = ?", [flask.request.form.get('title', None), str(int(num) + int(move[0])), name, move[0]])
+
+                conn.commit()
+
+                return redirect('/w/' + url_pas(flask.request.form.get('title', None)))
+            else:
+                return re_error('/error/19')
+        else:
+            curs.execute("select data from data where title = ?", [name])
+            data = curs.fetchall()
+            if data:            
+                curs.execute("update data set title = ? where title = ?", [flask.request.form.get('title', None), name])
+                curs.execute("update back set link = ? where link = ?", [flask.request.form.get('title', None), name])
+                
+                data_in = data[0][0]
+            else:
+                data_in = ''
+                
+            history_plus(
+                name, 
+                data_in, 
+                get_time(), 
+                ip_check(), 
+                flask.request.form.get('send', None) + ' (<a>' + name + '</a> - <a>' + flask.request.form.get('title', None) + '</a> move)', 
+                '0'
+            )
+            
+            curs.execute("update back set type = 'no' where title = ? and not type = 'cat' and not type = 'no'", [name])
+            curs.execute("delete from back where title = ? and not type = 'cat' and type = 'no'", [flask.request.form.get('title', None)])
+
+            curs.execute("update history set title = ? where title = ?", [flask.request.form.get('title', None), name])
+            conn.commit()
+
+            return redirect('/w/' + url_pas(flask.request.form.get('title', None)))
+    else:            
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [name, wiki_set(), custom(), other2([' (' + load_lang('move') + ')', 0])],
+            data =  '''
+                    <form method="post">
+                        ''' + ip_warring() + '''
+                        <input placeholder="''' + load_lang('document_name') + '" value="' + name + '''" name="title" type="text">
+                        <hr class=\"main_hr\">
+                        <input placeholder="''' + load_lang('why') + '''" name="send" type="text">
+                        <hr class=\"main_hr\">
+                        ''' + captcha_get() + '''
+                        <button type="submit">''' + load_lang('move') + '''</button>
+                    </form>
+                    ''',
+            menu = [['w/' + url_pas(name), load_lang('return')]]
+        ))

+ 67 - 0
route/need_email.py

@@ -0,0 +1,67 @@
+from .tool.func import *
+
+def need_email_2(conn, tool):
+    curs = conn.cursor()
+
+    if flask.request.method == 'POST':
+        if tool == 'need_email':
+            if 'c_id' in flask.session:
+                main_email = ['naver.com', 'gmail.com', 'daum.net', 'hanmail.net', 'hanmail2.net']
+                data = re.search('@([^@]+)$', flask.request.form.get('email', ''))
+                if data:
+                    data = data.groups()[0]
+
+                    curs.execute("select html from html_filter where html = ? and kind = 'email'", [data])
+                    if curs.fetchall() or (data in main_email):
+                        curs.execute('select id from user_set where name = "email" and data = ?', [flask.request.form.get('email', '')])
+                        if curs.fetchall():
+                            flask.session.pop('c_id', None)
+                            flask.session.pop('c_pw', None)
+                            flask.session.pop('c_key', None)
+
+                            return redirect('/register')
+                        else:
+                            send_email(flask.request.form.get('email', ''), wiki_set()[0] + ' key', 'key : ' + flask.session['c_key'])
+                            flask.session['c_email'] = flask.request.form.get('email', '')
+
+                            return redirect('/check_key')
+
+            return redirect('/register')
+        else:
+            curs.execute("select id from user where id = ? and email = ?", [flask.request.form.get('id', ''), flask.request.form.get('email', '')])
+            if curs.fetchall():
+                flask.session['c_key'] = ''.join(random.choice("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") for i in range(16))
+                flask.session['c_id'] = flask.request.form.get('id', '')
+
+                send_email(flask.request.form.get('email', ''), wiki_set()[0] + ' ' + load_lang('password_search') + ' key', 'key : ' + flask.session['c_key'])
+
+                return redirect('/check_pass_key')
+    else:
+        if tool == 'need_email':
+            return easy_minify(flask.render_template(skin_check(),    
+                imp = [load_lang('email'), wiki_set(), custom(), other2([0, 0])],
+                data =  '''
+                        <a href="/email_filter">(''' + load_lang('email_filter_list') + ''')</a>
+                        <hr class=\"main_hr\">
+                        <form method="post">
+                            <input placeholder="''' + load_lang('email') + '''" name="email" type="text">
+                            <hr class=\"main_hr\">
+                            <button type="submit">''' + load_lang('save') + '''</button>
+                        </form>
+                        ''',
+                menu = [['user', load_lang('return')]]
+            ))
+        else:
+            return easy_minify(flask.render_template(skin_check(),    
+                imp = [load_lang('password_search'), wiki_set(), custom(), other2([0, 0])],
+                data =  '''
+                        <form method="post">
+                            <input placeholder="''' + load_lang('id') + '''" name="id" type="text">
+                            <hr class=\"main_hr\">
+                            <input placeholder="email" name="email" type="text">
+                            <hr class=\"main_hr\">
+                            <button type="submit">''' + load_lang('save') + '''</button>
+                        </form>
+                        ''',
+                menu = [['user', load_lang('return')]]
+            ))

+ 19 - 0
route/not_close_topic.py

@@ -0,0 +1,19 @@
+from .tool.func import *
+
+def not_close_topic_2(conn):
+    curs = conn.cursor()
+
+    div = '<ul>'
+    
+    curs.execute('select title, sub from rd where stop != "O" order by date desc')
+    n_list = curs.fetchall()
+    for data in n_list:
+        div += '<li><a href="/topic/' + url_pas(data[0]) + '/sub/' + url_pas(data[1]) + '">' + html.escape(data[0]) + ' (' + data[1] + ')</a></li>'
+            
+    div += '</ul>'
+
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [load_lang('open_discussion_list'), wiki_set(), custom(), other2([0, 0])],
+        data = div,
+        menu = [['manager', load_lang('return')]]
+    ))

+ 57 - 0
route/now_update.py

@@ -0,0 +1,57 @@
+from .tool.func import *
+
+def now_update_2(conn):
+    curs = conn.cursor()
+
+    if admin_check() != 1:
+        return re_error('/error/3')
+
+    if flask.request.method == 'POST':
+        admin_check(None, 'update')
+
+        curs.execute('select data from other where name = "update"')
+        up_data = curs.fetchall()
+        if up_data:
+            up_data = up_data[0][0]
+        else:
+            up_data = 'stable'
+
+        if platform.system() == 'Linux':
+            print('Update')
+
+            os.system('git remote rm origin')
+            os.system('git remote add origin https://github.com/2DU/opennamu.git')
+            ok = os.system('git fetch origin ' + up_data)
+            ok = os.system('git reset --hard origin/' + up_data)
+            if ok == 0:
+                return redirect('/restart')
+        else:
+            if platform.system() == 'Windows':
+                print('Update')
+
+                urllib.request.urlretrieve('https://github.com/2DU/opennamu/archive/' + up_data + '.zip', 'update.zip')
+                zipfile.ZipFile('update.zip').extractall('')
+                ok = os.system('xcopy /y /r opennamu-' + up_data + ' .')
+                if ok == 0:
+                    print('Remove')
+                    os.system('rd /s /q opennamu-' + up_data)
+                    os.system('del update.zip')
+
+                    return redirect('/restart')
+
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [load_lang('update'), wiki_set(), custom(), other2([0, 0])],
+            data = load_lang("update_error") + ' <a href="https://github.com/2DU/opennamu">(Github)</a>',
+            menu = [['manager/1', load_lang('return')]]
+        ))
+    else:
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [load_lang('update'), wiki_set(), custom(), other2([0, 0])],
+            data =  '''
+                    <form method="post">
+                        <button type="submit">''' + load_lang('update') + '''</button>
+                    </form>
+                    ''',
+            menu = [['manager', load_lang('return')]]
+        ))
+

+ 100 - 0
route/oauth_setting.py

@@ -0,0 +1,100 @@
+from .tool.func import *
+
+def oauth_setting_2(conn):
+    curs = conn.cursor()
+
+    if admin_check(None, 'oauth setting') != 1:
+        return re_error('/error/3')
+
+    if flask.request.method == 'POST':
+        try:
+            facebook_client_id = flask.request.form['facebook_client_id']
+            facebook_client_secret = flask.request.form['facebook_client_secret']
+            
+            naver_client_id = flask.request.form['naver_client_id']
+            naver_client_secret = flask.request.form['naver_client_secret']
+        except:
+            return easy_minify(flask.render_template(skin_check(),
+                imp = [load_lang('inter_error'), wiki_set(), custom(), other2([0, 0])],
+                data = '<h2>ie_no_data_required</h2>' + load_lang('ie_no_data_required'),
+                menu = [['other', load_lang('return')]]
+            ))
+
+        with open(app_var['path_oauth_setting'], 'r', encoding='utf-8') as f:
+            legacy = json.loads(f.read())
+
+        with open(app_var['path_oauth_setting'], 'w', encoding='utf-8') as f:
+            f.write('''
+                {
+                    "_README" : {
+                        "en" : "''' + legacy['_README']['en'] + '''",
+                        "ko" : "''' + legacy['_README']['ko'] + '''",
+                        "support" : ''' + str(legacy['_README']['support']).replace("'", '"') + '''
+                    },
+                    "publish_url" : "''' + legacy['publish_url'] + '''",
+                    "facebook" : {
+                        "client_id" : "''' + facebook_client_id + '''",
+                        "client_secret" : "''' + facebook_client_secret + '''"
+                    },
+                    "naver" : {
+                        "client_id" : "''' + naver_client_id + '''",
+                        "client_secret" : "''' + naver_client_secret + '''"
+                    }
+                }
+            ''')
+        
+        return flask.redirect('/oauth_setting')
+
+    oauth_supported = load_oauth('_README')['support']
+
+    body_content = ''
+    body_content += '''
+        <script>
+            function check_value (target) {
+                target_box = document.getElementById(target.id + "_box");
+                if (target.value !== "") {
+                    target_box.checked = true;
+                } else {
+                    target_box.checked = false;
+                } 
+            }
+        </script>
+    '''
+
+    init_js = ''
+    body_content += '<form method="post">'
+
+    for i in range(len(oauth_supported)):
+        oauth_data = load_oauth(oauth_supported[i])
+        for j in range(2):
+            if j == 0:
+                load_target = 'id'
+            elif j == 1:
+                load_target = 'secret'
+
+            init_js += 'check_value(document.getElementById("{}_client_{}"));'.format(oauth_supported[i], load_target)
+
+            body_content += '''
+                <input id="{}_client_{}_box" type="checkbox" disabled>
+                <input placeholder="{}_client_{}" id="{}_client_{}" name="{}_client_{}" value="{}" type="text" onChange="check_value(this)" style="width: 80%;">
+                <hr>
+            '''.format(
+                oauth_supported[i],
+                load_target,
+                oauth_supported[i], 
+                load_target, 
+                oauth_supported[i], 
+                load_target, 
+                oauth_supported[i], 
+                load_target, 
+                oauth_data['client_{}'.format(load_target)]
+            )
+    
+    body_content += '<button id="save" type="submit">' + load_lang('save') + '</button></form>'
+    body_content += '<script>' + init_js + '</script>'
+    
+    return easy_minify(flask.render_template(skin_check(),
+        imp = [load_lang('oauth_setting'), wiki_set(), custom(), other2([0, 0])],
+        data = body_content,
+        menu = [['other', load_lang('return')]]
+    ))

+ 45 - 0
route/other.py

@@ -0,0 +1,45 @@
+from .tool.func import *
+
+def other_2(conn, r_ver):
+    curs = conn.cursor()
+    
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [load_lang('other_tool'), wiki_set(), custom(), other2([0, 0])],
+        data =  '''
+                <h2>''' + load_lang('record') + '''</h2>
+                <ul>
+                    <li><a href="/manager/6">''' + load_lang('edit_record') + '''</a></li>
+                    <li><a href="/manager/7">''' + load_lang('discussion_record') + '''</a></li>
+                </ul>
+                <br>
+                <h2>''' + load_lang('list') + '''</h2>
+                <ul>
+                    <li><a href="/admin_list">''' + load_lang('admin_list') + '''</a></li>
+                    <li><a href="/give_log">''' + load_lang('admin_group_list') + '''</a></li>
+                    <li><a href="/not_close_topic">''' + load_lang('open_discussion_list') + '''</a></li>
+                    <li><a href="/title_index">''' + load_lang('all_document_list') + '''</a></li>
+                    <li><a href="/acl_list">''' + load_lang('acl_document_list') + '''</a></li>
+                    <li><a href="/please">''' + load_lang('need_document') + '''</a></li>
+                    <li><a href="/block_log">''' + load_lang('recent_ban') + '''</a></li>
+                    <li><a href="/user_log">''' + load_lang('member_list') + '''</a></li>
+                    <li><a href="/admin_log">''' + load_lang('authority_use_list') + '''</a></li>
+                </ul>
+                <br>
+                <h2>''' + load_lang('other') + '''</h2>
+                <ul>
+                    <li><a href="/upload">''' + load_lang('upload') + '''</a></li>
+                    <li><a href="/manager/10">''' + load_lang('search') + '''</a></li>
+                </ul>
+                <br>
+                <h2>''' + load_lang('admin') + '''</h2>
+                <ul>
+                    <li><a href="/manager/1">''' + load_lang('admin_tool') + '''</a></li>
+                </ul>
+                <br>
+                <h2>''' + load_lang('version') + '''</h2>
+                <ul>
+                    <li>''' + load_lang('version') + ' : <a id="out_link" href="https://github.com/2DU/opennamu/blob/master/version.md">' + r_ver + '''</a></li>
+                </ul>
+                ''',
+        menu = 0
+    ))

+ 29 - 0
route/please.py

@@ -0,0 +1,29 @@
+from .tool.func import *
+
+def please_2(conn):
+    curs = conn.cursor()
+
+    num = int(number_check(flask.request.args.get('num', '1')))
+    if num * 50 > 0:
+        sql_num = num * 50 - 50
+    else:
+        sql_num = 0
+        
+    div = '<ul>'
+    var = ''
+    
+    curs.execute("select distinct title from back where type = 'no' order by title asc limit ?, '50'", [str(sql_num)])
+    data_list = curs.fetchall()
+    for data in data_list:
+        if var != data[0]:
+            div += '<li><a id="not_thing" href="/w/' + url_pas(data[0]) + '">' + data[0] + '</a></li>'   
+
+            var = data[0]
+        
+    div += '</ul>' + next_fix('/please?num=', num, data_list)
+    
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [load_lang('need_document'), wiki_set(), custom(), other2([0, 0])],
+        data = div,
+        menu = [['other', load_lang('return')]]
+    ))

+ 92 - 0
route/plus_inter.py

@@ -0,0 +1,92 @@
+from .tool.func import *
+
+def plus_inter_2(conn, tools, name):
+    curs = conn.cursor()
+    
+    if flask.request.method == 'POST':
+        if tools == 'plus_inter_wiki':
+            curs.execute('insert into inter (title, link) values (?, ?)', [flask.request.form.get('title', None), flask.request.form.get('link', None)])
+            admin_check(None, 'inter_wiki_plus')
+        elif tools == 'plus_edit_filter':
+            if admin_check(1, 'edit_filter edit') != 1:
+                return re_error('/error/3')
+
+            if flask.request.form.get('limitless', '') != '':
+                end = 'X'
+            else:
+                end = flask.request.form.get('second', 'X')
+
+            curs.execute("select name from filter where name = ?", [name])
+            if curs.fetchall():
+                curs.execute("update filter set regex = ?, sub = ? where name = ?", [flask.request.form.get('content', 'test'), end, name])
+            else:
+                curs.execute("insert into filter (name, regex, sub) values (?, ?, ?)", [name, flask.request.form.get('content', 'test'), end])
+        else:
+            if tools == 'plus_name_filter':
+                admin_check(None, 'name_filter edit')
+                type_d = 'name'
+            else:
+                admin_check(None, 'email_filter edit')
+                type_d = 'email'
+            
+            curs.execute('insert into html_filter (html, kind) values (?, ?)', [flask.request.form.get('title', 'test'), type_d])
+        
+        conn.commit()
+    
+        return redirect('/' + re.sub('^plus_', '', tools))
+    else:
+        if admin_check(1) != 1:
+            stat = 'disabled'
+        else:
+            stat = ''
+
+        if tools == 'plus_inter_wiki':
+            title = load_lang('interwiki_add')
+            form_data = '''
+                        <input placeholder="''' + load_lang('name') + '''" type="text" name="title">
+                        <hr class=\"main_hr\">
+                        <input placeholder="link" type="text" name="link">
+                        '''
+        elif tools == 'plus_edit_filter':
+            curs.execute("select regex, sub from filter where name = ?", [name])
+            exist = curs.fetchall()
+            if exist:
+                textarea = exist[0][0]
+                
+                if exist[0][1] == 'X':
+                    time_check = 'checked="checked"'
+                    time_data = ''
+                else:
+                    time_check = ''
+                    time_data = exist[0][1]
+            else:
+                textarea = ''
+                time_check = ''
+                time_data = ''
+
+            title = load_lang('edit_filter_add')
+            form_data = '''
+                        <input placeholder="''' + load_lang('second') + '''" name="second" type="text" value="''' + html.escape(time_data) + '''">
+                        <hr class=\"main_hr\">
+                        <input ''' + stat + ''' type="checkbox" ''' + time_check + ''' name="limitless"> ''' + load_lang('limitless') + '''
+                        <hr class=\"main_hr\">
+                        <input ''' + stat + ''' placeholder="''' + load_lang('regex') + '''" name="content" value="''' + html.escape(textarea) + '''" type="text">
+                        '''
+        elif tools == 'plus_name_filter':
+            title = load_lang('id_filter_add')
+            form_data = '<input placeholder="' + load_lang('id') + ' ' + load_lang('regex') + '" type="text" name="title">'
+        else:
+            title = load_lang('email_filter_add')
+            form_data = '<input placeholder="email" type="text" name="title">'
+
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [title, wiki_set(), custom(), other2([0, 0])],
+            data =  '''
+                    <form method="post">
+                        ''' + form_data + '''
+                        <hr class=\"main_hr\">
+                        <button ''' + stat + ''' type="submit">''' + load_lang('add') + '''</button>
+                    </form>
+                    ''',
+            menu = [[re.sub('^plus_', '', tools), load_lang('return')]]
+        ))

+ 41 - 0
route/preview.py

@@ -0,0 +1,41 @@
+from .tool.func import *
+
+def preview_2(conn, name):
+    curs = conn.cursor()
+
+    if acl_check(name) == 1:
+        return re_error('/ban')
+         
+    new_data = re.sub('^\r\n', '', flask.request.form.get('content', None))
+    new_data = re.sub('\r\n$', '', new_data)
+    
+    end_data = render_set(
+        title = name,
+        data = new_data
+    )
+    
+    if flask.request.args.get('section', None):
+        action = '?section=' + flask.request.args.get('section', None)
+    else:
+        action = ''
+
+    js_data = edit_help_button()
+    
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [name, wiki_set(), custom(), other2([' (' + load_lang('preview') + ')', 0])],
+        data =  '<a href="/edit_filter">(' + load_lang('edit_filter_rule') + ')</a>' + js_data[0] + '''
+                <form method="post" action="/edit/''' + url_pas(name) + action + '''">
+                    ''' + js_data[1] + '''
+                    <textarea id="content" rows="25" name="content">''' + html.escape(flask.request.form.get('content', None)) + '''</textarea>
+                    <textarea style="display: none;" name="otent">''' + html.escape(flask.request.form.get('otent', None)) + '''</textarea>
+                    <hr class=\"main_hr\">
+                    <input placeholder="''' + load_lang('why') + '''" name="send" type="text">
+                    <hr class=\"main_hr\">
+                    ''' + captcha_get() + '''
+                    <button id="save" type="submit">''' + load_lang('save') + '''</button>
+                    <button id="preview" type="submit" formaction="/preview/''' + url_pas(name) + action + '">' + load_lang('preview') + '''</button>
+                </form>
+                <hr class=\"main_hr\">
+                ''' + end_data,
+        menu = [['w/' + url_pas(name), load_lang('return')]]
+    ))

+ 47 - 0
route/raw_view.py

@@ -0,0 +1,47 @@
+from .tool.func import *
+
+def raw_view_2(conn, name, sub_title, num):
+    curs = conn.cursor()
+
+    v_name = name
+    sub = ' (' + load_lang('raw') + ')'
+    
+    if not num:
+        num = flask.request.args.get('num', None)
+        if num:
+            num = int(number_check(num))
+    
+    if not sub_title and num:
+        curs.execute("select title from history where title = ? and id = ? and hide = 'O'", [name, str(num)])
+        if curs.fetchall() and admin_check(6) != 1:
+            return re_error('/error/3')
+        
+        curs.execute("select data from history where title = ? and id = ?", [name, str(num)])
+        
+        sub += ' (r' + str(num) + ')'
+
+        menu = [['history/' + url_pas(name), load_lang('history')]]
+    elif sub_title:
+        curs.execute("select data from topic where id = ? and title = ? and sub = ? and block = ''", [str(num), name, sub_title])
+        
+        v_name = load_lang('discussion_raw')
+        sub = ' (' + str(num) + ')'
+
+        menu = [['topic/' + url_pas(name) + '/sub/' + url_pas(sub_title) + '#' + str(num), load_lang('discussion')], ['topic/' + url_pas(name) + '/sub/' + url_pas(sub_title) + '/admin/' + str(num), load_lang('return')]]
+    else:
+        curs.execute("select data from data where title = ?", [name])
+        
+        menu = [['w/' + url_pas(name), load_lang('return')]]
+
+    data = curs.fetchall()
+    if data:
+        p_data = html.escape(data[0][0])
+        p_data = '<textarea readonly rows="25">' + p_data + '</textarea>'
+        
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [v_name, wiki_set(), custom(), other2([sub, 0])],
+            data = p_data,
+            menu = menu
+        ))
+    else:
+        return redirect('/w/' + url_pas(name))

+ 172 - 0
route/read_view.py

@@ -0,0 +1,172 @@
+from .tool.func import *
+
+def read_view_2(conn, name):
+    curs = conn.cursor()
+
+    data_none = 0
+    sub = ''
+    acl = ''
+    div = ''
+
+    num = flask.request.args.get('num', None)
+    if num:
+        num = int(number_check(num))
+    else:
+        if not flask.request.args.get('from', None):
+            curs.execute("select title from back where link = ? and type = 'redirect'", [name])
+            redirect_data = curs.fetchall()
+            if redirect_data:
+                return redirect('/w/' + redirect_data[0][0] + '?from=' + 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') + ')'
+
+    curs.execute("select link from back where title = ? and type = 'cat' order by link asc", [name])
+                
+    curs.execute("select title from data where title like ?", ['%' + name + '/%'])
+    if curs.fetchall():
+        down = 1
+    else:
+        down = 0
+        
+    m = re.search("^(.*)\/(.*)$", name)
+    if m:
+        uppage = m.groups()[0]
+    else:
+        uppage = 0
+        
+    if re.search('^category:', name):        
+        curs.execute("select link from back where title = ? and type = 'cat' order by link asc", [name])
+        back = curs.fetchall()
+        if back:
+            div = '<br><h2 id="cate_normal">' + load_lang('category') + '</h2><ul>'
+            u_div = ''
+
+            for data in back:    
+                if re.search('^category:', data[0]):
+                    u_div += '<li><a href="/w/' + url_pas(data[0]) + '">' + data[0] + '</a></li>'
+                else:
+                    curs.execute("select title from back where title = ? and type = 'include'", [data[0]])
+                    db_data = curs.fetchall()
+                    if db_data:
+                        div += '<li><a href="/w/' + url_pas(data[0]) + '">' + data[0] + '</a> <a id="inside" href="/xref/' + url_pas(data[0]) + '">(' + load_lang('backlink') + ')</a></li>'
+                    else: 
+                        div += '<li><a href="/w/' + url_pas(data[0]) + '">' + data[0] + '</a></li>'
+
+            div += '</ul>'
+            
+            if div == '<br><h2 id="cate_normal">' + load_lang('category') + '</h2><ul></ul>':
+                div = ''
+            
+            if u_div != '':
+                div += '<br><h2 id="cate_under">' + load_lang('under_category') + '</h2><ul>' + u_div + '</ul>'
+
+
+    if num:
+        curs.execute("select title from history where title = ? and id = ? and hide = 'O'", [name, str(num)])
+        if curs.fetchall() and admin_check(6) != 1:
+            return redirect('/history/' + url_pas(name))
+
+        curs.execute("select title, data from history where title = ? and id = ?", [name, str(num)])
+    else:
+        curs.execute("select title, data from data where title = ?", [name])
+    
+    data = curs.fetchall()
+    if data:
+        else_data = data[0][1]
+        response_data = 200
+    else:
+        data_none = 1
+        response_data = 404
+        else_data = None
+
+    m = re.search("^user:([^/]*)", name)
+    if m:
+        g = m.groups()
+        
+        curs.execute("select acl from user where id = ?", [g[0]])
+        test = curs.fetchall()
+        if test and test[0][0] != 'user':
+            acl = ' (' + load_lang('admin') + ')'
+        else:
+            if ban_check(g[0]) == 1:
+                sub += ' (' + load_lang('blocked') + ')'
+            else:
+                acl = ''
+
+    curs.execute("select dec from acl where title = ?", [name])
+    data = curs.fetchall()
+    if data:
+        acl += ' (' + load_lang('acl') + ')'
+            
+    if flask.request.args.get('from', None) and else_data:
+        else_data = re.sub('^\r\n', '', else_data)
+        else_data = re.sub('\r\n$', '', else_data)
+            
+    end_data = render_set(
+        title = name,
+        data = else_data
+    )
+
+    if end_data == 'HTTP Request 401.3':
+        response_data = 401
+    
+    if num:
+        menu = [['history/' + url_pas(name), load_lang('history')]]
+        sub = ' (r' + str(num) + ')'
+        acl = ''
+        r_date = 0
+    else:
+        if data_none == 1:
+            menu = [['edit/' + url_pas(name), load_lang('create')]]
+        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')]]
+
+        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 + '''
+                        </div>
+                        <br>''' + end_data
+
+        if uppage != 0:
+            menu += [['w/' + url_pas(uppage), load_lang('upper')]]
+
+        if down:
+            menu += [['down/' + url_pas(name), load_lang('sub')]]
+    
+        curs.execute("select date from history where title = ? order by date desc limit 1", [name])
+        date = curs.fetchall()
+        if date:
+            r_date = date[0][0]
+        else:
+            r_date = 0
+
+    div = end_data + div
+            
+    adsense_code = '<div align="center" style="display: block; margin-bottom: 10px;">{}</div>'
+
+    curs.execute("select data from other where name = 'adsense'")
+    adsense_enabled = curs.fetchall()[0][0]
+    if adsense_enabled == 'True':
+        curs.execute("select data from other where name = 'adsense_code'")
+        adsense_code = adsense_code.format(curs.fetchall()[0][0])
+    else:
+        adsense_code = adsense_code.format('')
+
+    curs.execute("select data from other where name = 'body'")
+    body = curs.fetchall()
+    if body:
+        div = body[0][0] + '<hr class=\"main_hr\">' + div
+    
+    div = adsense_code + '<div>' + div + '</div>'
+    
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [flask.request.args.get('show', name), wiki_set(), custom(), other2([sub + acl, r_date])],
+        data = div,
+        menu = menu
+    )), response_data

+ 178 - 0
route/recent_changes.py

@@ -0,0 +1,178 @@
+from .tool.func import *
+
+def recent_changes_2(conn, name, tool):
+    curs = conn.cursor()
+
+    if flask.request.method == 'POST':
+        return redirect('/diff/' + url_pas(name) + '?first=' + flask.request.form.get('b', None) + '&second=' + flask.request.form.get('a', None))
+    else:
+        one_admin = admin_check(1)
+        six_admin = admin_check(6)
+        
+        ban = ''
+        select = ''
+
+        div =   '''
+                <table id="main_table_set">
+                    <tbody>
+                        <tr>
+                '''
+        
+        if name:
+            num = int(number_check(flask.request.args.get('num', '1')))
+            if num * 50 > 0:
+                sql_num = num * 50 - 50
+            else:
+                sql_num = 0      
+
+            if tool == 'history':
+                div +=  '''
+                        <td id="main_table_width">''' + load_lang('version') + '''</td>
+                        <td id="main_table_width">''' + load_lang('editor') + '''</td>
+                        <td id="main_table_width">''' + load_lang('time') + '''</td></tr>
+                        '''
+                
+                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>
+                            <td id="main_table_width">''' + load_lang('editor') + '''</td>
+                            <td id="main_table_width">''' + load_lang('time') + '''</td>
+                        </tr>
+                        '''
+
+                div = '<a href="/topic_record/' + url_pas(name) + '">(' + load_lang('discussion') + ')</a><hr class=\"main_hr\">' + div
+                
+                curs.execute("select id, title, date, ip, send, leng from history where ip = ? order by date desc limit ?, '50'", [name, str(sql_num)])
+        else:
+            num = int(number_check(flask.request.args.get('num', '1')))
+            if num * 50 > 0:
+                sql_num = num * 50 - 50
+            else:
+                sql_num = 0            
+            
+            div +=  '''
+                        <td id="main_table_width">''' + load_lang('document_name') + '''</td>
+                        <td id="main_table_width">''' + load_lang('editor') + '''</td>
+                        <td id="main_table_width">''' + load_lang('time') + '''</td>
+                    </tr>
+                    '''
+            
+            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)])
+
+        data_list = curs.fetchall()
+        for data in data_list:    
+            select += '<option value="' + data[0] + '">' + data[0] + '</option>'     
+            send = '<br>'
+            
+            if data[4]:
+                if not re.search("^(?: *)$", data[4]):
+                    send = data[4]
+            
+            if re.search("\+", data[5]):
+                leng = '<span style="color:green;">(' + data[5] + ')</span>'
+            elif re.search("\-", data[5]):
+                leng = '<span style="color:red;">(' + data[5] + ')</span>'
+            else:
+                leng = '<span style="color:gray;">(' + data[5] + ')</span>'
+                
+            ip = ip_pas(data[3])
+            if int(data[0]) - 1 == 0:
+                revert = ''
+            else:
+                revert = '<a href="/diff/' + url_pas(data[1]) + '?first=' + str(int(data[0]) - 1) + '&second=' + data[0] + '">(' + load_lang('compare') + ')</a> <a href="/revert/' + url_pas(data[1]) + '?num=' + str(int(data[0]) - 1) + '">(' + load_lang('revert') + ')</a>'
+            
+            style = ['', '']
+            date = data[2]
+
+            curs.execute("select title from history where title = ? and id = ? and hide = 'O'", [data[1], data[0]])
+            hide = curs.fetchall()
+            
+            if six_admin == 1:
+                if hide:                            
+                    hidden = ' <a href="/hidden/' + url_pas(data[1]) + '?num=' + data[0] + '">(' + load_lang('hide_release') + ')'
+                    
+                    style[0] = 'id="toron_color_grey"'
+                    style[1] = 'id="toron_color_grey"'
+                    
+                    if send == '<br>':
+                        send = '(' + load_lang('hide') + ')'
+                    else:
+                        send += ' (' + load_lang('hide') + ')'
+                else:
+                    hidden = ' <a href="/hidden/' + url_pas(data[1]) + '?num=' + data[0] + '">(' + load_lang('hide') + ')'
+            elif not hide:
+                hidden = ''
+            else:
+                ip = ''
+                hidden = ''
+                ban = ''
+                date = ''
+
+                send = '(' + load_lang('hide') + ')'
+
+                style[0] = 'style="display: none;"'
+                style[1] = 'id="toron_color_grey"'
+
+            if tool == 'history':
+                title = '<a href="/w/' + url_pas(name) + '?num=' + data[0] + '">r' + data[0] + '</a> <a href="/raw/' + url_pas(name) + '?num=' + data[0] + '">(' + load_lang('raw') + ')</a> '
+            else:
+                title = '<a href="/w/' + url_pas(data[1]) + '">' + html.escape(data[1]) + '</a> <a href="/history/' + url_pas(data[1]) + '">(r' + data[0] + ')</a> '
+                    
+            div +=  '''
+                    <tr ''' + style[0] + '''>
+                        <td>''' + title + revert + ' ' + leng + '''</td>
+                        <td>''' + ip + ban + hidden + '''</td>
+                        <td>''' + date + '''</td>
+                    </tr>
+                    <tr ''' + style[1] + '''>
+                        <td colspan="3">''' + send_parser(send) + '''</td>
+                    </tr>
+                    '''
+
+        div +=  '''
+                    </tbody>
+                </table>
+                '''
+        sub = ''
+
+        if name:
+            if tool == 'history':
+                div =   '''
+                        <form method="post">
+                            <select name="a">''' + select + '''</select> <select name="b">''' + select + '''</select>
+                            <button type="submit">''' + load_lang('compare') + '''</button>
+                        </form>
+                        <hr class=\"main_hr\">
+                        ''' + div
+                title = name
+                
+                sub += ' (' + load_lang('history') + ')'
+                
+                menu = [['w/' + url_pas(name), load_lang('document')], ['raw/' + url_pas(name), 'raw']]
+                
+                div += next_fix('/history/' + url_pas(name) + '?num=', num, data_list)
+            else:
+                curs.execute("select end from ban where block = ?", [name])
+                if curs.fetchall():
+                    sub += ' (' + load_lang('blocked') + ')'
+
+                title = load_lang('edit_record')
+                
+                menu = [['other', load_lang('other')], ['user', load_lang('user')], ['count/' + url_pas(name), load_lang('count')]]
+                
+                div += next_fix('/record/' + url_pas(name) + '?num=', num, data_list)
+        else:
+            menu = 0
+            title = load_lang('recent_change')
+                
+            div += next_fix('/recent_changes?num=', num, data_list)
+        
+        if sub == '':
+            sub = 0
+                
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [title, wiki_set(), custom(), other2([sub, 0])],
+            data = div,
+            menu = menu
+        ))

+ 44 - 0
route/recent_discuss.py

@@ -0,0 +1,44 @@
+from .tool.func import *
+
+def recent_discuss_2(conn):
+    curs = conn.cursor()
+
+    div = ''
+    
+    if flask.request.args.get('what', 'normal') == 'normal':
+        div += '<a href="/recent_discuss?what=close">(' + load_lang('close_discussion') + ')</a>'
+       
+        m_sub = 0
+    else:
+        div += '<a href="/recent_discuss">(' + load_lang('open_discussion') + ')</a>'
+        
+        m_sub = ' (' + load_lang('closed') + ')'
+
+    div +=  '''
+            <hr class=\"main_hr\">
+            <table id="main_table_set">
+                <tbody>
+                    <tr>
+                        <td id="main_table_width_half">''' + load_lang('discussion_name') + '''</td>
+                        <td id="main_table_width_half">''' + load_lang('time') + '''</td>
+                    </tr>
+            '''
+    
+    if m_sub == 0:
+        curs.execute("select title, sub, date from rd where not stop = 'O' order by date desc limit 50")
+    else:
+        curs.execute("select title, sub, date from rd where stop = 'O' order by date desc limit 50")
+        
+    for data in curs.fetchall():
+        title = html.escape(data[0])
+        sub = html.escape(data[1])
+
+        div += '<tr><td><a href="/topic/' + url_pas(data[0]) + '/sub/' + url_pas(data[1]) + '">' + title + '</a> (' + sub + ')</td><td>' + data[2] + '</td></tr>'
+    
+    div += '</tbody></table>'
+            
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [load_lang('recent_discussion'), wiki_set(), custom(), other2([m_sub, 0])],
+        data = div,
+        menu = 0
+    ))

+ 109 - 0
route/register.py

@@ -0,0 +1,109 @@
+from .tool.func import *
+
+def register_2(conn):
+    curs = conn.cursor()
+
+    if ban_check() == 1:
+        return re_error('/ban')
+
+    if custom()[2] != 0:
+        return redirect('/user')
+
+    if not admin_check() == 1:
+        curs.execute('select data from other where name = "reg"')
+        set_d = curs.fetchall()
+        if set_d and set_d[0][0] == 'on':
+            return re_error('/ban')
+    
+    if flask.request.method == 'POST': 
+        if captcha_post(flask.request.form.get('g-recaptcha-response', '')) == 1:
+            return re_error('/error/13')
+        else:
+            captcha_post('', 0)
+
+        if flask.request.form.get('pw', None) != flask.request.form.get('pw2', None):
+            return re_error('/error/20')
+
+        if re.search('(?:[^A-Za-zㄱ-힣0-9 ])', flask.request.form.get('id', None)):
+            return re_error('/error/8')
+            
+        curs.execute('select html from html_filter where kind = "name"')
+        set_d = curs.fetchall()
+        for i in set_d:
+            check_r = re.compile(i[0], re.I)
+            if check_r.search(flask.request.form.get('id', None)):
+                return re_error('/error/8')
+
+        if len(flask.request.form.get('id', None)) > 32:
+            return re_error('/error/7')
+
+        curs.execute("select id from user where id = ?", [flask.request.form.get('id', None)])
+        if curs.fetchall():
+            return re_error('/error/6')
+
+        hashed = pw_encode(flask.request.form.get('pw', None))
+        
+        curs.execute('select data from other where name = "email_have"')
+        sql_data = curs.fetchall()
+        if sql_data and sql_data[0][0] != '':
+            flask.session['c_id'] = flask.request.form.get('id', None)
+            flask.session['c_pw'] = hashed
+            flask.session['c_key'] = ''.join(random.choice("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") for i in range(16))
+
+            return redirect('/need_email')
+        else:
+            curs.execute('select data from other where name = "encode"')
+            db_data = curs.fetchall()
+
+            curs.execute("select id from user limit 1")
+            if not curs.fetchall():
+                curs.execute("insert into user (id, pw, acl, date, encode) values (?, ?, 'owner', ?, ?)", [flask.request.form.get('id', None), hashed, get_time(), db_data[0][0]])
+
+                first = 1
+            else:
+                curs.execute("insert into user (id, pw, acl, date, encode) values (?, ?, 'user', ?, ?)", [flask.request.form.get('id', None), hashed, get_time(), db_data[0][0]])
+
+                first = 0
+
+            ip = ip_check()
+            agent = flask.request.headers.get('User-Agent')
+
+            curs.execute("insert into ua_d (name, ip, ua, today, sub) values (?, ?, ?, ?, '')", [flask.request.form.get('id', None), ip, agent, get_time()])  
+
+            flask.session['state'] = 1
+            flask.session['id'] = flask.request.form.get('id', None)
+            flask.session['head'] = ''
+                  
+            conn.commit()
+            
+            if first == 0:
+                return redirect('/change')
+            else:
+                return redirect('/setting')
+    else:        
+        contract = ''
+        
+        curs.execute('select data from other where name = "contract"')
+        data = curs.fetchall()
+        if data and data[0][0] != '':
+            contract = data[0][0] + '<hr class=\"main_hr\">'
+
+        return easy_minify(flask.render_template(skin_check(),    
+            imp = [load_lang('register'), wiki_set(), custom(), other2([0, 0])],
+            data =  '''
+                    <form method="post">
+                        ''' + contract + '''
+                        <input placeholder="''' + load_lang('id') + '''" name="id" type="text">
+                        <hr class=\"main_hr\">
+                        <input placeholder="''' + load_lang('password') + '''" name="pw" type="password">
+                        <hr class=\"main_hr\">
+                        <input placeholder="''' + load_lang('password_confirm') + '''" name="pw2" type="password">
+                        <hr class=\"main_hr\">
+                        ''' + captcha_get() + '''
+                        <button type="submit">''' + load_lang('save') + '''</button>
+                        <hr class=\"main_hr\">
+                        <span>''' + load_lang('http_warring') + '''</span>
+                    </form>
+                    ''',
+            menu = [['user', load_lang('return')]]
+        ))

+ 24 - 0
route/restart.py

@@ -0,0 +1,24 @@
+from .tool.func import *
+
+def restart_2(conn):
+    curs = conn.cursor()
+
+    if admin_check() != 1:
+        return re_error('/error/3')
+
+    if flask.request.method == 'POST':
+        admin_check(None, 'restart')
+
+        print('Restart')
+
+        os.execl(sys.executable, sys.executable, *sys.argv)
+    else:
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [load_lang('wiki_restart'), wiki_set(), custom(), other2([0, 0])],
+            data =  '''
+                    <form method="post">
+                        <button type="submit">''' + load_lang('restart') + '''</button>
+                    </form>
+                    ''',
+            menu = [['manager', load_lang('return')]]
+        ))

+ 77 - 0
route/revert.py

@@ -0,0 +1,77 @@
+from .tool.func import *
+
+def revert_2(conn, name):
+    curs = conn.cursor()
+
+    num = int(number_check(flask.request.args.get('num', '1')))
+
+    curs.execute("select title from history where title = ? and id = ? and hide = 'O'", [name, str(num)])
+    if curs.fetchall() and admin_check(6) != 1:
+        return re_error('/error/3')
+
+    if acl_check(name) == 1:
+        return re_error('/ban')
+
+    if flask.request.method == 'POST':
+        if captcha_post(flask.request.form.get('g-recaptcha-response', '')) == 1:
+            return re_error('/error/13')
+        else:
+            captcha_post('', 0)
+    
+        curs.execute("select data from history where title = ? and id = ?", [name, str(num)])
+        data = curs.fetchall()
+        if data:
+            if edit_filter_do(data[0][0]) == 1:
+                return re_error('/error/21')
+
+        curs.execute("delete from back where link = ?", [name])
+        conn.commit()
+        
+        if data:                                
+            curs.execute("select data from data where title = ?", [name])
+            data_old = curs.fetchall()
+            if data_old:
+                leng = leng_check(len(data_old[0][0]), len(data[0][0]))
+                curs.execute("update data set data = ? where title = ?", [data[0][0], name])
+            else:
+                leng = '+' + str(len(data[0][0]))
+                curs.execute("insert into data (title, data) values (?, ?)", [name, data[0][0]])
+                
+            history_plus(
+                name, 
+                data[0][0], 
+                get_time(), 
+                ip_check(), 
+                flask.request.form.get('send', None) + ' (r' + str(num) + ')', 
+                leng
+            )
+
+            render_set(
+                title = name,
+                data = data[0][0],
+                num = 1
+            )
+            
+            conn.commit()
+            
+        return redirect('/w/' + url_pas(name))
+    else:
+        curs.execute("select title from history where title = ? and id = ?", [name, str(num)])
+        if not curs.fetchall():
+            return redirect('/w/' + url_pas(name))
+
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [name, wiki_set(), custom(), other2([' (' + load_lang('revert') + ')', 0])],
+            data =  '''
+                    <form method="post">
+                        <span>r''' + flask.request.args.get('num', '0') + '''</span>
+                        <hr class=\"main_hr\">
+                        ''' + ip_warring() + '''
+                        <input placeholder="''' + load_lang('why') + '''" name="send" type="text">
+                        <hr class=\"main_hr\">
+                        ''' + captcha_get() + '''
+                        <button type="submit">''' + load_lang('revert') + '''</button>
+                    </form>
+                    ''',
+            menu = [['history/' + url_pas(name), load_lang('history')], ['recent_changes', load_lang('recent_change')]]
+        ))

+ 403 - 0
route/setting.py

@@ -0,0 +1,403 @@
+from .tool.func import *
+
+def setting_2(conn, num):
+    curs = conn.cursor()
+
+    if num != 0 and admin_check() != 1:
+        return re_error('/ban')
+
+    if num == 0:
+        li_list = [
+            load_lang('main_setting'),
+            load_lang('text_setting'),
+            load_lang('main_head'),
+            load_lang('main_body'),
+            'robots.txt',
+            'Google'
+        ]
+        
+        x = 0
+        
+        li_data = ''
+        
+        for li in li_list:
+            x += 1
+            li_data += '<li><a href="/setting/' + str(x) + '">' + li + '</a></li>'
+
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [load_lang('setting'), wiki_set(), custom(), other2([0, 0])],
+            data = '<h2>' + load_lang('list') + '</h2><ul>' + li_data + '</ul>',
+            menu = [['manager', load_lang('return')]]
+        ))
+    elif num == 1:
+        i_list = ['name', 'logo', 'frontpage', 'license', 'upload', 'skin', 'edit', 'reg', 'ip_view', 'back_up', 'port', 'key', 'update', 'email_have', 'discussion', 'encode', 'host']
+        n_list = ['wiki', '', 'FrontPage', 'CC 0', '2', '', 'normal', '', '', '0', '3000', 'test', 'stable', '', 'normal', 'sha256', '0.0.0.0']
+        
+        if flask.request.method == 'POST':
+            i = 0
+            
+            for data in i_list:
+                curs.execute("update other set data = ? where name = ?", [flask.request.form.get(data, n_list[i]), data])
+                i += 1
+
+            conn.commit()
+
+            admin_check(None, 'edit_set')
+
+            return redirect('/setting/1')
+        else:
+            d_list = []
+            
+            x = 0
+            
+            for i in i_list:
+                curs.execute('select data from other where name = ?', [i])
+                sql_d = curs.fetchall()
+                if sql_d:
+                    d_list += [sql_d[0][0]]
+                else:
+                    curs.execute('insert into other (name, data) values (?, ?)', [i, n_list[x]])
+                    
+                    d_list += [n_list[x]]
+
+                x += 1
+
+            conn.commit()
+            
+            div = ''
+            acl_list = [[load_lang('member'), 'login'], [load_lang('ip'), 'normal'], [load_lang('admin'), 'admin']]
+            for i in acl_list:
+                if i[1] == d_list[6]:
+                    div = '<option value="' + i[1] + '">' + i[0] + '</option>' + div
+                else:
+                    div += '<option value="' + i[1] + '">' + i[0] + '</option>'
+
+            div4 = ''
+            for i in acl_list:
+                if i[1] == d_list[14]:
+                    div4 = '<option value="' + i[1] + '">' + i[0] + '</option>' + div4
+                else:
+                    div4 += '<option value="' + i[1] + '">' + i[0] + '</option>'
+
+            ch_1 = ''
+            if d_list[7]:
+                ch_1 = 'checked="checked"'
+
+            ch_2 = ''
+            if d_list[8]:
+                ch_2 = 'checked="checked"'
+            
+            ch_3 = ''
+            if d_list[13]:
+                ch_3 = 'checked="checked"'
+
+            div2 = load_skin(d_list[5])
+
+            div3 =''
+            if d_list[12] == 'stable':
+                div3 += '<option value="stable">stable</option>'
+                div3 += '<option value="master">master</option>'
+            else:
+                div3 += '<option value="master">master</option>'
+                div3 += '<option value="stable">stable</option>'
+                
+            div5 =''
+            encode_data = ['sha256', 'sha3']
+            for i in encode_data:
+                if d_list[15] == i:
+                    div5 = '<option value="' + i + '">' + i + '</option>' + div5
+                else:
+                    div5 += '<option value="' + i + '">' + i + '</option>'
+
+            return easy_minify(flask.render_template(skin_check(), 
+                imp = [load_lang('main_setting'), wiki_set(), custom(), other2([0, 0])],
+                data =  '''
+                        <form method="post">
+                            <span>''' + load_lang('wiki_name') + '''</span>
+                            <br>
+                            <br>
+                            <input placeholder="''' + load_lang('wiki_name') + '''" type="text" name="name" value="''' + html.escape(d_list[0]) + '''">
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('wiki_logo') + ''' (HTML)</span>
+                            <br>
+                            <br>
+                            <input placeholder="''' + load_lang('wiki_logo') + '''" type="text" name="logo" value="''' + html.escape(d_list[1]) + '''">
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('main_page') + '''</span>
+                            <br>
+                            <br>
+                            <input placeholder="''' + load_lang('main_page') + '''" type="text" name="frontpage" value="''' + html.escape(d_list[2]) + '''">
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('bottom_text') + ''' (HTML)</span>
+                            <br>
+                            <br>
+                            <input placeholder="''' + load_lang('bottom_text') + '''" type="text" name="license" value="''' + html.escape(d_list[3]) + '''">
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('max_file_size') + ''' [MB]</span>
+                            <br>
+                            <br>
+                            <input placeholder="''' + load_lang('max_file_size') + '''" type="text" name="upload" value="''' + html.escape(d_list[4]) + '''">
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('backup_interval') + ' [' + load_lang('hour') + '''] (off : 0) {restart}</span>
+                            <br>
+                            <br>
+                            <input placeholder="''' + load_lang('backup_interval') + '''" type="text" name="back_up" value="''' + html.escape(d_list[9]) + '''">
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('wiki_skin') + '''</span>
+                            <br>
+                            <br>
+                            <select name="skin">''' + div2 + '''</select>
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('default_acl') + '''</span>
+                            <br>
+                            <br>
+                            <select name="edit">''' + div + '''</select>
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('default_discussion_acl') + '''</span>
+                            <br>
+                            <br>
+                            <select name="discussion">''' + div4 + '''</select>
+                            <hr class=\"main_hr\">
+                            <input type="checkbox" name="reg" ''' + ch_1 + '''> ''' + load_lang('no_register') + '''
+                            <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/5">''' + load_lang('google_imap_required') + '''</a>}
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('wiki_host') + '''</span>
+                            <br>
+                            <br>
+                            <input placeholder="''' + load_lang('wiki_host') + '''" type="text" name="host" value="''' + html.escape(d_list[16]) + '''">
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('wiki_port') + '''</span>
+                            <br>
+                            <br>
+                            <input placeholder="''' + load_lang('wiki_port') + '''" type="text" name="port" value="''' + html.escape(d_list[10]) + '''">
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('wiki_secret_key') + '''</span>
+                            <br>
+                            <br>
+                            <input placeholder="''' + load_lang('wiki_secret_key') + '''" type="password" name="key" value="''' + html.escape(d_list[11]) + '''">
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('update_branch') + '''</span>
+                            <br>
+                            <br>
+                            <select name="update">''' + div3 + '''</select>
+                            <hr class=\"main_hr\">
+                            <span>encryption method</span>
+                            <br>
+                            <br>
+                            <select name="encode">''' + div5 + '''</select>
+                            <hr class=\"main_hr\">
+                            <button id="save" type="submit">''' + load_lang('save') + '''</button>
+                        </form>
+                        ''',
+                menu = [['setting', load_lang('return')]]
+            ))
+    elif num == 2:
+        if flask.request.method == 'POST':
+            curs.execute("update other set data = ? where name = ?", [flask.request.form.get('contract', None), 'contract'])
+            curs.execute("update other set data = ? where name = ?", [flask.request.form.get('no_login_warring', None), 'no_login_warring'])
+            conn.commit()
+            
+            admin_check(None, 'edit_set')
+
+            return redirect('/setting/2')
+        else:
+            i_list = ['contract', 'no_login_warring']
+            n_list = ['', '']
+            d_list = []
+            
+            x = 0
+            
+            for i in i_list:
+                curs.execute('select data from other where name = ?', [i])
+                sql_d = curs.fetchall()
+                if sql_d:
+                    d_list += [sql_d[0][0]]
+                else:
+                    curs.execute('insert into other (name, data) values (?, ?)', [i, n_list[x]])
+                    
+                    d_list += [n_list[x]]
+
+                x += 1
+
+            conn.commit()
+
+            return easy_minify(flask.render_template(skin_check(), 
+                imp = [load_lang('text_setting'), wiki_set(), custom(), other2([0, 0])],
+                data =  '''
+                        <form method="post">
+                            <span>''' + load_lang('register_text') + '''</span>
+                            <br>
+                            <br>
+                            <input placeholder="''' + load_lang('register_text') + '''" type="text" name="contract" value="''' + html.escape(d_list[0]) + '''">
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('non_login_alert') + '''</span>
+                            <br>
+                            <br>
+                            <input placeholder="''' + load_lang('non_login_alert') + '''" type="text" name="no_login_warring" value="''' + html.escape(d_list[1]) + '''">
+                            <hr class=\"main_hr\">
+                            <button id="save" type="submit">''' + load_lang('save') + '''</button>
+                        </form>
+                        ''',
+                menu = [['setting', load_lang('return')]]
+            ))
+    elif num == 3 or num == 4:
+        if flask.request.method == 'POST':
+            if num == 4:
+                info_d = 'body'
+                end_r = '4'
+            else:
+                info_d = 'head'
+                end_r = '3'
+            
+            curs.execute("select name from other where name = ?", [info_d])
+            if curs.fetchall():
+                curs.execute("update other set data = ? where name = ?", [flask.request.form.get('content', ''), info_d])
+            else:
+                curs.execute("insert into other (name, data) values (?, ?)", [info_d, flask.request.form.get('content', '')])
+            
+            conn.commit()
+
+            admin_check(None, 'edit_set')
+
+            return redirect('/setting/' + end_r)
+        else:
+            if num == 4:
+                curs.execute("select data from other where name = 'body'")
+                title = '_body'
+                start = ''
+            else:
+                curs.execute("select data from other where name = 'head'")
+                title = '_head'
+                start = '<span>&lt;style&gt;CSS&lt;/style&gt;<br>&lt;script&gt;JS&lt;/script&gt;</span><hr class=\"main_hr\">'
+                
+            head = curs.fetchall()
+            if head:
+                data = head[0][0]
+            else:
+                data = ''
+
+            return easy_minify(flask.render_template(skin_check(), 
+                imp = [load_lang(data = 'main' + title, safe = 1), wiki_set(), custom(), other2([0, 0])],
+                data =  '''
+                        <form method="post">
+                            ''' + start + '''
+                            <textarea rows="25" name="content">''' + html.escape(data) + '''</textarea>
+                            <hr class=\"main_hr\">
+                            <button id="save" type="submit">''' + load_lang('save') + '''</button>
+                        </form>
+                        ''',
+                menu = [['setting', load_lang('return')]]
+            ))
+    elif num == 5:
+        if flask.request.method == 'POST':
+            curs.execute("select name from other where name = 'robot'")
+            if curs.fetchall():
+                curs.execute("update other set data = ? where name = 'robot'", [flask.request.form.get('content', None)])
+            else:
+                curs.execute("insert into other (name, data) values ('robot', ?)", [flask.request.form.get('content', None)])
+            
+            conn.commit()
+            
+            fw = open('./robots.txt', 'w')
+            fw.write(re.sub('\r\n', '\n', flask.request.form.get('content', None)))
+            fw.close()
+            
+            admin_check(None, 'edit_set')
+
+            return redirect('/setting/4')
+        else:
+            curs.execute("select data from other where name = 'robot'")
+            robot = curs.fetchall()
+            if robot:
+                data = robot[0][0]
+            else:
+                data = ''
+
+            f = open('./robots.txt', 'r')
+            lines = f.readlines()
+            f.close()
+
+            if not data or data == '':
+                data = ''.join(lines)
+
+            return easy_minify(flask.render_template(skin_check(), 
+                imp = ['robots.txt', wiki_set(), custom(), other2([0, 0])],
+                data =  '''
+                        <a href="/robots.txt">(view)</a>
+                        <hr class=\"main_hr\">
+                        <form method="post">
+                            <textarea rows="25" name="content">''' + html.escape(data) + '''</textarea>
+                            <hr class=\"main_hr\">
+                            <button id="save" type="submit">''' + load_lang('save') + '''</button>
+                        </form>
+                        ''',
+                menu = [['setting', load_lang('return')]]
+            ))
+    elif num == 6:
+        i_list = ['recaptcha', 'sec_re', 'g_email', 'g_pass']
+
+        if flask.request.method == 'POST':
+            for data in i_list:
+                curs.execute("update other set data = ? where name = ?", [flask.request.form.get(data, ''), data])
+
+            conn.commit()
+            
+            admin_check(None, 'edit_set')
+
+            return redirect('/setting/5')
+        else:
+            n_list = ['', '', '', '']
+            d_list = []
+            
+            x = 0
+            
+            for i in i_list:
+                curs.execute('select data from other where name = ?', [i])
+                sql_d = curs.fetchall()
+                if sql_d:
+                    d_list += [sql_d[0][0]]
+                else:
+                    curs.execute('insert into other (name, data) values (?, ?)', [i, n_list[x]])
+                    
+                    d_list += [n_list[x]]
+
+                x += 1
+
+            conn.commit()
+
+            return easy_minify(flask.render_template(skin_check(), 
+                imp = ['google', wiki_set(), custom(), other2([0, 0])],
+                data =  '''
+                        <form method="post">
+                            <h2><a href="https://www.google.com/recaptcha/admin">recaptcha</a></h2>
+                            <span>''' + load_lang('recaptcha') + ''' (HTML)</span>
+                            <br>
+                            <br>
+                            <input placeholder="''' + load_lang('recaptcha') + ''' (HTML)" type="text" name="recaptcha" value="''' + html.escape(d_list[0]) + '''">
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('recaptcha') + ' (' + load_lang('secret_key') + ''')</span>
+                            <br>
+                            <br>
+                            <input placeholder="''' + load_lang('recaptcha') + ' (' + load_lang('secret_key') + ''')" type="text" name="sec_re" value="''' + html.escape(d_list[1]) + '''">
+                            <hr class=\"main_hr\">
+                            <h2><a href="https://support.google.com/mail/answer/7126229">''' + load_lang('google_imap') + '</a> {' + load_lang('restart_required') + '''}</h1>
+                            <span>''' + load_lang('google_email') + '''</span>
+                            <br>
+                            <br>
+                            <input placeholder="''' + load_lang('google_email') + '''" type="text" name="g_email" value="''' + html.escape(d_list[2]) + '''">
+                            <hr class=\"main_hr\">
+                            <span><a href="https://security.google.com/settings/security/apppasswords">''' + load_lang('google_app_password') + '''</a></span>
+                            <br>
+                            <br>
+                            <input placeholder="''' + load_lang('google_app_password') + '''" type="password" name="g_pass" value="''' + html.escape(d_list[3]) + '''">
+                            <hr class=\"main_hr\">
+                            <button id="save" type="submit">''' + load_lang('save') + '''</button>
+                        </form>
+                        ''',
+                menu = [['setting', load_lang('return')]]
+            ))
+    else:
+        return redirect()

+ 72 - 0
route/title_index.py

@@ -0,0 +1,72 @@
+from .tool.func import *
+
+def title_index_2(conn):
+    curs = conn.cursor()
+    
+    page = int(number_check(flask.request.args.get('page', '1')))
+    num = int(number_check(flask.request.args.get('num', '100')))
+    if page * num > 0:
+        sql_num = page * num - num
+    else:
+        sql_num = 0
+
+    all_list = sql_num + 1
+
+    if num > 1000:
+        return re_error('/error/3')
+
+    data = '<a href="/title_index?num=250">(250)</a> <a href="/title_index?num=500">(500)</a> <a href="/title_index?num=1000">(1000)</a>'
+
+    curs.execute("select title from data order by title asc limit ?, ?", [str(sql_num), str(num)])
+    title_list = curs.fetchall()
+    if title_list:
+        data += '<hr class=\"main_hr\"><ul>'
+
+    for list_data in title_list:
+        data += '<li>' + str(all_list) + '. <a href="/w/' + url_pas(list_data[0]) + '">' + list_data[0] + '</a></li>'        
+        all_list += 1
+
+    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 + '%'])
+            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]]
+        
+        data += '''
+                </ul>
+                <hr class=\"main_hr\">
+                <ul>
+                    <li>all : ''' + str(count_end[0]) + '''</li>
+                </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>
+                '''
+
+    data += '</ul>' + next_fix('/title_index?num=' + str(num) + '&page=', page, title_list, num)
+    sub = ' (' + str(num) + ')'
+    
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [load_lang('all_document_list'), wiki_set(), custom(), other2([sub, 0])],
+        data = data,
+        menu = [['other', load_lang('return')]]
+    ))

+ 177 - 99
func.py → route/tool/func.py

@@ -1,15 +1,30 @@
+import werkzeug.routing
+import flask_compress
+import flask_reggie
+import tornado.ioloop
+import tornado.httpserver
+import tornado.wsgi
+import urllib.request
 import email.mime.text
 import email.mime.text
 import urllib.request
 import urllib.request
 import sqlite3
 import sqlite3
 import hashlib
 import hashlib
 import smtplib
 import smtplib
 import bcrypt
 import bcrypt
+import platform
+import zipfile
+import difflib
+import shutil
+import threading
+import logging
+import random
 import flask
 import flask
 import json
 import json
 import html
 import html
 import sys
 import sys
 import re
 import re
 import os
 import os
+
 try:
 try:
     import css_html_js_minify
     import css_html_js_minify
 except:
 except:
@@ -18,8 +33,10 @@ except:
 if sys.version_info < (3, 6):
 if sys.version_info < (3, 6):
     import sha3
     import sha3
 
 
-from set_mark.tool import *
-from mark import *
+from .set_mark.tool import *
+from .mark import *
+
+app_var = json.loads(open('data/app_variables.json', encoding='utf-8').read())
 
 
 def load_conn(data):
 def load_conn(data):
     global conn
     global conn
@@ -53,7 +70,41 @@ def send_email(who, title, data):
 
 
         smtp.quit()
         smtp.quit()
     except:
     except:
-        print('error : email login error')
+        print('Error : Email login error')
+
+def last_change(data):
+    json_address = re.sub("\.html$", ".json", skin_check())
+    try:
+        json_data = json.loads(open(json_address).read())
+    except:
+        json_data = 0
+
+    if json_data != 0:
+        for j_data in json_data:
+            if "class" in json_data[j_data]:
+                if "require" in json_data[j_data]:
+                    re_data = re.compile("<((?:" + j_data + ")( (?:(?!>).)*)?)>")
+                    s_data = re_data.findall(data)
+                    for i_data in s_data:
+                        e_data = 0
+
+                        for j_i_data in json_data[j_data]["require"]:
+                            re_data_2 = re.compile("( |^)" + j_i_data + " *= *[\'\"]" + json_data[j_data]["require"][j_i_data] + "[\'\"]")
+                            if not re_data_2.search(i_data[1]):
+                                re_data_2 = re.compile("( |^)" + j_i_data + "=" + json_data[j_data]["require"][j_i_data] + "(?: |$)")
+                                if not re_data_2.search(i_data[1]):
+                                    e_data = 1
+
+                                    break
+
+                        if e_data == 0:
+                            re_data_3 = re.compile("<" + i_data[0] + ">")
+                            data = re_data_3.sub("<" + i_data[0] + " class=\"" + json_data[j_data]["class"] + "\">", data)        
+                else:
+                    re_data = re.compile("<(?P<in>" + j_data + "(?: (?:(?!>).)*)?)>")
+                    data = re_data.sub("<\g<in> class=\"" + json_data[j_data]["class"] + "\">", data)        
+
+    return data
 
 
 def easy_minify(data, tool = None):
 def easy_minify(data, tool = None):
     try:
     try:
@@ -68,11 +119,13 @@ def easy_minify(data, tool = None):
         data = re.sub('\n +<', '\n<', data)
         data = re.sub('\n +<', '\n<', data)
         data = re.sub('>(\n| )+<', '> <', data)
         data = re.sub('>(\n| )+<', '> <', data)
     
     
-    return data
+    return last_change(data)
 
 
-def render_set(title = '', data = '', num = 0):
+def render_set(title = '', data = '', num = 0, s_data = 0):
     if acl_check(title, 'render') == 1:
     if acl_check(title, 'render') == 1:
-        return 'http request 401.3'
+        return 'HTTP Request 401.3'
+    elif s_data == 1:
+        return data
     else:
     else:
         return namumark(title, data, num)
         return namumark(title, data, num)
 
 
@@ -86,50 +139,11 @@ def captcha_get():
             curs.execute('select data from other where name = "sec_re"')
             curs.execute('select data from other where name = "sec_re"')
             sec_re = curs.fetchall()
             sec_re = curs.fetchall()
             if sec_re and sec_re[0][0] != '':
             if sec_re and sec_re[0][0] != '':
-                data += recaptcha[0][0] + '<hr>'
+                data += recaptcha[0][0] + '<hr class=\"main_hr\">'
 
 
     return data
     return data
 
 
 def update():
 def update():
-    # v3.0.5 사용자 문서, 파일 문서, 분류 문서 영어화
-    try:
-        all_rep = [['사용자:', 'user:'], ['파일:', 'file:'], ['분류:', 'category:']]
-        all_rep2 = ['data', 'history', 'acl', 'topic', 'back']
-
-        test = 0
-
-        for i in range(3):
-            for j in range(6):
-                if not j == 5:
-                    curs.execute('select title from ' + all_rep2[j] + ' where title like ?', [all_rep[i][0] + '%'])
-                else:
-                    curs.execute('select link from back where link like ?', [all_rep[i][0] + '%'])
-
-                user_rep = curs.fetchall()
-                if user_rep:
-                    for user_rep2 in user_rep:
-                        test = 1
-
-                        first = re.sub('^' + all_rep[i][0], all_rep[i][1], user_rep2[0])
-
-                        if j == 0:
-                            curs.execute("update data set title = ? where title = ?", [first, user_rep2[0]])
-                        elif j == 1:
-                            curs.execute("update history set title = ? where title = ?", [first, user_rep2[0]])
-                        elif j == 2:
-                            curs.execute("update acl set title = ? where title = ?", [first, user_rep2[0]])
-                        elif j == 3:
-                            curs.execute("update topic set title = ? where title = ?", [first, user_rep2[0]])
-                        elif j == 4:
-                            curs.execute("update back set title = ? where title = ?", [first, user_rep2[0]])
-                        elif j == 5:
-                            curs.execute("update back set link = ? where link = ?", [first, user_rep2[0]])
-
-        if test == 1:
-            print('사용자 to user, 파일 to file, 분류 to category')
-    except:
-        pass
-        
     # v3.0.8 rd, agreedis, stop 테이블 통합
     # v3.0.8 rd, agreedis, stop 테이블 통합
     try:
     try:
         curs.execute("select title, sub, close from stop")
         curs.execute("select title, sub, close from stop")
@@ -154,6 +168,32 @@ def update():
     except:
     except:
         pass
         pass
 
 
+    # Start Data Migration Code
+    app_var = json.loads(open(os.path.abspath('./data/app_variables.json'), encoding='utf-8').read())
+
+    if os.path.exists('image'):
+        os.rename('image', app_var['path_data_image'])
+
+    if os.path.exists('oauthsettings.json'):
+        os.rename('oauthsettings.json', app_var['path_oauth_setting'])
+
+    try:
+        load_oauth('discord')
+    except KeyError:
+        old_oauth_data = json.loads(open(app_var['path_oauth_setting'], encoding='utf-8').read())
+
+        if 'discord' not in old_oauth_data['_README']['support']:
+            old_oauth_data['_README']['support'] += ['discord']
+
+        old_oauth_data['discord'] = {}
+        old_oauth_data['discord']['client_id'] = ''
+        old_oauth_data['discord']['client_secret'] = ''
+
+        with open(app_var['path_oauth_setting'], 'w') as f:
+            f.write(json.dumps(old_oauth_data, sort_keys = True, indent = 4))
+
+    # -> End Data Migration Code
+
 def pw_encode(data, data2 = '', type_d = ''):
 def pw_encode(data, data2 = '', type_d = ''):
     if type_d == '':
     if type_d == '':
         curs.execute('select data from other where name = "encode"')
         curs.execute('select data from other where name = "encode"')
@@ -188,17 +228,25 @@ def pw_check(data, data2, type_d = 'no', id_d = ''):
     else:
     else:
         set_data = db_data[0][0]
         set_data = db_data[0][0]
     
     
-    if set_data in ['sha256', 'sha3']:
-        data3 = pw_encode(data = data, type_d = set_data)
-        if data3 == data2:
-            re_data = 1
-        else:
-            re_data = 0
-    else:
-        if pw_encode(data, data2, 'bcrypt') == data2:
-            re_data = 1
+    while 1:
+        if set_data in ['sha256', 'sha3']:
+            data3 = pw_encode(data = data, type_d = set_data)
+            if data3 == data2:
+                re_data = 1
+            else:
+                re_data = 0
+
+            break
         else:
         else:
-            re_data = 0
+            try:
+                if pw_encode(data, data2, 'bcrypt') == data2:
+                    re_data = 1
+                else:
+                    re_data = 0
+
+                break
+            except:
+                set_data = db_data[0][0]
 
 
     if db_data[0][0] != set_data and re_data == 1 and id_d != '':
     if db_data[0][0] != set_data and re_data == 1 and id_d != '':
         curs.execute("update user set pw = ?, encode = ? where id = ?", [pw_encode(data), db_data[0][0], id_d])
         curs.execute("update user set pw = ?, encode = ? where id = ?", [pw_encode(data), db_data[0][0], id_d])
@@ -228,7 +276,7 @@ def captcha_post(re_data, num = 1):
     else:
     else:
         pass
         pass
 
 
-def load_lang(data, num = 2):
+def load_lang(data, num = 2, safe = 0):
     if num == 1:
     if num == 1:
         curs.execute("select data from other where name = 'language'")
         curs.execute("select data from other where name = 'language'")
         rep_data = curs.fetchall()
         rep_data = curs.fetchall()
@@ -237,9 +285,12 @@ def load_lang(data, num = 2):
         lang = json.loads(json_data)
         lang = json.loads(json_data)
 
 
         if data in lang:
         if data in lang:
-            return lang[data]
+            if safe == 1:
+                return lang[data]
+            else:
+                return html.escape(lang[data])
         else:
         else:
-            return data + ' (missing)'
+            return html.escape(data + ' (M)')
     else:
     else:
         curs.execute('select data from user_set where name = "lang" and id = ?', [ip_check()])
         curs.execute('select data from user_set where name = "lang" and id = ?', [ip_check()])
         rep_data = curs.fetchall()
         rep_data = curs.fetchall()
@@ -248,14 +299,31 @@ def load_lang(data, num = 2):
                 json_data = open(os.path.join('language', rep_data[0][0] + '.json'), 'rt', encoding='utf-8').read()
                 json_data = open(os.path.join('language', rep_data[0][0] + '.json'), 'rt', encoding='utf-8').read()
                 lang = json.loads(json_data)
                 lang = json.loads(json_data)
             except:
             except:
-                return load_lang(data, 1)
+                return load_lang(data, 1, safe)
 
 
             if data in lang:
             if data in lang:
-                return lang[data]
+                if safe == 1:
+                    return lang[data]
+                else:
+                    return html.escape(lang[data])
             else:
             else:
-                return load_lang(data, 1)
+                return load_lang(data, 1, safe)
         else:
         else:
-            return load_lang(data, 1)
+            return load_lang(data, 1, safe)
+
+def load_oauth(provider):
+    oauth = json.loads(open(app_var['path_oauth_setting'], encoding='utf-8').read())
+
+    return oauth[provider]
+
+def update_oauth(provider, target, content):
+    oauth = json.loads(open(app_var['path_oauth_setting'], encoding='utf-8').read())
+    oauth[provider][target] = content
+
+    with open(app_var['path_oauth_setting'], 'w') as f:
+        f.write(json.dumps(oauth, sort_keys=True, indent=4))
+
+    return 'Done'
 
 
 def ip_or_user(data):
 def ip_or_user(data):
     if re.search('(\.|:)', data):
     if re.search('(\.|:)', data):
@@ -266,24 +334,24 @@ def ip_or_user(data):
 def edit_help_button():
 def edit_help_button():
     # https://stackoverflow.com/questions/11076975/insert-text-into-textarea-at-cursor-position-javascript
     # https://stackoverflow.com/questions/11076975/insert-text-into-textarea-at-cursor-position-javascript
     js_data =   '''
     js_data =   '''
-                <script>
-                    function insert_data(name, data) {
-                        if(document.selection) { 
-                            document.getElementById(name).focus();
-
-                            sel = document.selection.createRange();
-                            sel.text = data; 
-                        } else if(document.getElementById(name).selectionStart || document.getElementById(name).selectionStart == '0') {
-                            var startPos = document.getElementById(name).selectionStart;
-                            var endPos = document.getElementById(name).selectionEnd;
-
-                            document.getElementById(name).value = document.getElementById(name).value.substring(0, startPos) + data + document.getElementById(name).value.substring(endPos, document.getElementById(name).value.length); 
-                        } else {
-                            document.getElementById(name).value += data;
-                        }
-                    }
-                </script>
-                '''
+        <script>
+            function insert_data(name, data) {
+                if(document.selection) { 
+                    document.getElementById(name).focus();
+
+                    sel = document.selection.createRange();
+                    sel.text = data; 
+                } else if(document.getElementById(name).selectionStart || document.getElementById(name).selectionStart == '0') {
+                    var startPos = document.getElementById(name).selectionStart;
+                    var endPos = document.getElementById(name).selectionEnd;
+
+                    document.getElementById(name).value = document.getElementById(name).value.substring(0, startPos) + data + document.getElementById(name).value.substring(endPos, document.getElementById(name).value.length); 
+                } else {
+                    document.getElementById(name).value += data;
+                }
+            }
+        </script>
+    '''
 
 
     insert_list = [['[[|]]', '[[|]]'], ['[*()]', '[*()]'], ['{{{#!}}}', '{{{#!}}}'], ['||<>||', '||<>||'], ["\\'\\'\\'", "\'\'\'"]]
     insert_list = [['[[|]]', '[[|]]'], ['[*()]', '[*()]'], ['{{{#!}}}', '{{{#!}}}'], ['||<>||', '||<>||'], ["\\'\\'\\'", "\'\'\'"]]
 
 
@@ -291,16 +359,16 @@ def edit_help_button():
     for insert_data in insert_list:
     for insert_data in insert_list:
         data += '<a href="javascript:void(0);" onclick="insert_data(\'content\', \'' + insert_data[0] + '\');">(' + insert_data[1] + ')</a> '
         data += '<a href="javascript:void(0);" onclick="insert_data(\'content\', \'' + insert_data[0] + '\');">(' + insert_data[1] + ')</a> '
 
 
-    return [js_data, data + '<hr>']
+    return [js_data, data + '<hr class=\"main_hr\">']
 
 
 def ip_warring():
 def ip_warring():
     if custom()[2] == 0:    
     if custom()[2] == 0:    
         curs.execute('select data from other where name = "no_login_warring"')
         curs.execute('select data from other where name = "no_login_warring"')
         data = curs.fetchall()
         data = curs.fetchall()
         if data and data[0][0] != '':
         if data and data[0][0] != '':
-            text_data = '<span>' + data[0][0] + '</span><hr>'
+            text_data = '<span>' + data[0][0] + '</span><hr class=\"main_hr\">'
         else:
         else:
-            text_data = '<span>' + load_lang('no_login_warring') + '</span><hr>'
+            text_data = '<span>' + load_lang('no_login_warring') + '</span><hr class=\"main_hr\">'
     else:
     else:
         text_data = ''
         text_data = ''
 
 
@@ -328,11 +396,11 @@ def next_fix(link, num, page, end = 50):
 
 
     if num == 1:
     if num == 1:
         if len(page) == end:
         if len(page) == end:
-            list_data += '<hr><a href="' + link + str(num + 1) + '">(' + load_lang('next') + ')</a>'
+            list_data += '<hr class=\"main_hr\"><a href="' + link + str(num + 1) + '">(' + load_lang('next') + ')</a>'
     elif len(page) != end:
     elif len(page) != end:
-        list_data += '<hr><a href="' + link + str(num - 1) + '">(' + load_lang('previous') + ')</a>'
+        list_data += '<hr class=\"main_hr\"><a href="' + link + str(num - 1) + '">(' + load_lang('previous') + ')</a>'
     else:
     else:
-        list_data += '<hr><a href="' + link + str(num - 1) + '">(' + load_lang('previous') + ')</a> <a href="' + link + str(num + 1) + '">(' + load_lang('next') + ')</a>'
+        list_data += '<hr class=\"main_hr\"><a href="' + link + str(num - 1) + '">(' + load_lang('previous') + ')</a> <a href="' + link + str(num + 1) + '">(' + load_lang('next') + ')</a>'
 
 
     return list_data
     return list_data
 
 
@@ -348,7 +416,7 @@ def wiki_set(num = 1):
         if db_data and db_data[0][0] != '':
         if db_data and db_data[0][0] != '':
             data_list += [db_data[0][0]]
             data_list += [db_data[0][0]]
         else:
         else:
-            data_list += ['wiki']
+            data_list += ['Wiki']
 
 
         curs.execute('select data from other where name = "license"')
         curs.execute('select data from other where name = "license"')
         db_data = curs.fetchall()
         db_data = curs.fetchall()
@@ -372,7 +440,6 @@ def wiki_set(num = 1):
             data_list += [db_data[0][0]]
             data_list += [db_data[0][0]]
         else:
         else:
             data_list += ['']
             data_list += ['']
-
         return data_list
         return data_list
 
 
     if num == 2:
     if num == 2:
@@ -482,13 +549,13 @@ def ip_pas(raw_ip):
     hide = 0
     hide = 0
 
 
     if re.search("(\.|:)", raw_ip):
     if re.search("(\.|:)", raw_ip):
-        if not re.search("^" + load_lang('tool', 1) + ":", raw_ip):    
+        if not re.search("^tool:", raw_ip):    
             curs.execute("select data from other where name = 'ip_view'")
             curs.execute("select data from other where name = 'ip_view'")
             data = curs.fetchall()
             data = curs.fetchall()
             if data and data[0][0] != '':
             if data and data[0][0] != '':
                 ip = '<span style="font-size: 75%;">' + hashlib.md5(bytes(raw_ip, 'utf-8')).hexdigest() + '</span>'
                 ip = '<span style="font-size: 75%;">' + hashlib.md5(bytes(raw_ip, 'utf-8')).hexdigest() + '</span>'
 
 
-                if not admin_check('ban', None):
+                if not admin_check(1):
                     hide = 1
                     hide = 1
             else:
             else:
                 ip = raw_ip
                 ip = raw_ip
@@ -537,7 +604,7 @@ def custom():
     else:
     else:
         user_name = load_lang('user')
         user_name = load_lang('user')
 
 
-    return ['', '', user_icon, user_head, email, user_name, load_lang(data = '', num = 2)]
+    return ['', '', user_icon, user_head, email, user_name]
 
 
 def load_skin(data = ''):
 def load_skin(data = ''):
     div2 = ''
     div2 = ''
@@ -579,14 +646,14 @@ def acl_check(name, tool = ''):
         acl_data = curs.fetchall()
         acl_data = curs.fetchall()
         if acl_data:
         if acl_data:
             if acl_data[0][0] == 'user':
             if acl_data[0][0] == 'user':
-                if not user_data:
+                if ip_or_user(ip):
                     return 1
                     return 1
 
 
             if acl_data[0][0] == 'admin':
             if acl_data[0][0] == 'admin':
-                if not user_data:
+                if ip_or_user(ip):
                     return 1
                     return 1
 
 
-                if not admin_check(5, 'view (' + name + ')') == 1:
+                if admin_check(5, 'view (' + name + ')') != 1:
                     return 1
                     return 1
 
 
         return 0
         return 0
@@ -598,7 +665,7 @@ def acl_check(name, tool = ''):
         if acl_c:
         if acl_c:
             acl_n = acl_c.groups()
             acl_n = acl_c.groups()
 
 
-            if admin_check(5, None) == 1:
+            if admin_check(5) == 1:
                 return 0
                 return 0
 
 
             curs.execute("select dec from acl where title = ?", ['user:' + acl_n[0]])
             curs.execute("select dec from acl where title = ?", ['user:' + acl_n[0]])
@@ -650,7 +717,7 @@ def acl_check(name, tool = ''):
                 if not user_data:
                 if not user_data:
                     return 1
                     return 1
 
 
-                if not admin_check(5, None) == 1:
+                if not admin_check(5) == 1:
                     return 1
                     return 1
 
 
         return 0
         return 0
@@ -744,8 +811,10 @@ def ban_insert(name, end, why, login, blocker):
             login = ''
             login = ''
 
 
         if end != '0':
         if end != '0':
+            end = int(number_check(end))
+
             time = datetime.datetime.now()
             time = datetime.datetime.now()
-            plus = datetime.timedelta(seconds = int(end))
+            plus = datetime.timedelta(seconds = end)
             r_time = (time + plus).strftime("%Y-%m-%d %H:%M:%S")
             r_time = (time + plus).strftime("%Y-%m-%d %H:%M:%S")
         else:
         else:
             r_time = ''
             r_time = ''
@@ -778,6 +847,15 @@ def leng_check(first, second):
         
         
     return all_plus
     return all_plus
 
 
+def number_check(data):
+    if not data:
+        return '1'
+    else:
+        if re.search('[^0-9]', data):
+            return '1'
+        else:
+            return data
+
 def edit_filter_do(data):
 def edit_filter_do(data):
     if admin_check(1, 'edit_filter pass') != 1:
     if admin_check(1, 'edit_filter pass') != 1:
         curs.execute("select regex, sub from filter")
         curs.execute("select regex, sub from filter")
@@ -796,7 +874,7 @@ def edit_filter_do(data):
     
     
     return 0
     return 0
 
 
-def redirect(data):
+def redirect(data = '/'):
     return flask.redirect(data)
     return flask.redirect(data)
 
 
 def re_error(data):
 def re_error(data):

+ 68 - 0
route/tool/init.py

@@ -0,0 +1,68 @@
+import os
+
+env_dict = {
+    'host'      : os.getenv('NAMU_HOST'),
+    'port'      : os.getenv('NAMU_PORT'),
+    'language'  : os.getenv('NAMU_LANG'),
+    'markup'    : os.getenv('NAMU_MARKUP'),
+    'encode'    : os.getenv('NAMU_ENCRYPT')
+}
+
+server_set_var = {
+    'host' : {
+        'display' : 'Host',
+        'require' : 'conv',
+        'default' : '0.0.0.0'
+    },
+    'port' : {
+        'display' : 'Port',
+        'require' : 'conv',
+        'default' : '3000'
+    },
+    'language' : {
+        'display' : 'Language',
+        'require' : 'select',
+        'default' : 'en-US',
+        'list' : ['ko-KR', 'en-US']
+    },
+    'markup' : {
+        'display' : 'Markup',
+        'require' : 'select',
+        'default' : 'namumark',
+        'list' : ['namumark']
+    },
+    'encode' : {
+        'display' : 'Encrypt Method',
+        'require' : 'select',
+        'default' : 'sha3',
+        'list' : ['sha3', 'sha256']
+    }
+}
+
+def init(key):
+    if env_dict[key] != None:
+        return env_dict[key]
+    else:
+        while 1:
+            if server_set_var[key]['require'] == 'select':
+                list_ = '[' + ', '.join(server_set_var[key]['list']) + '] '
+            else:
+                list_ = ''
+
+            print('{} ({}) {}:'.format(
+                server_set_var[key]['display'],
+                server_set_var[key]['default'],
+                list_
+            ), end = '')
+
+            server_set_val = input()
+            if server_set_val:
+                if server_set_var[key]['require'] == 'select':
+                    if server_set_val not in server_set_var[key]['list']:
+                        pass
+                    else:
+                        return server_set_val
+                else:
+                    return server_set_val
+            else:
+                return server_set_var[key]['default']

+ 5 - 5
mark.py → route/tool/mark.py

@@ -1,4 +1,4 @@
-from set_mark.namu import namu
+from .set_mark.namu import namu
 
 
 import re
 import re
 import html
 import html
@@ -50,11 +50,11 @@ def namumark(title = '', data = None, num = 0):
 
 
         if num == 1:
         if num == 1:
             data_num = len(data[2]) 
             data_num = len(data[2]) 
-            data_in_num = int(data_num / 8)
+            data_in_num = int(data_num / multiprocessing.cpu_count())
             data_in = []
             data_in = []
 
 
-            for i in range(8):
-                if not i == 7:
+            for i in range(multiprocessing.cpu_count()):
+                if i != multiprocessing.cpu_count() - 1:
                     data_in += [data[2][data_in_num * i:data_in_num * (i + 1)]]
                     data_in += [data[2][data_in_num * i:data_in_num * (i + 1)]]
                 else:
                 else:
                     data_in += [data[2][data_in_num * i:]]
                     data_in += [data[2][data_in_num * i:]]
@@ -68,4 +68,4 @@ def namumark(title = '', data = None, num = 0):
             
             
         return data[0] + data[1]
         return data[0] + data[1]
     else:
     else:
-        return 'http request 404'
+        return 'HTTP Request 404'

+ 0 - 0
set_mark/namu.py → route/tool/set_mark/namu.py


+ 0 - 0
set_mark/tool.py → route/tool/set_mark/tool.py


+ 201 - 0
route/topic.py

@@ -0,0 +1,201 @@
+from .tool.func import *
+
+def topic_2(conn, name, sub):
+    curs = conn.cursor()
+    
+    ban = topic_check(name, sub)
+    admin = admin_check(3)
+    
+    if flask.request.method == 'POST':
+        if captcha_post(flask.request.form.get('g-recaptcha-response', '')) == 1:
+            return re_error('/error/13')
+        else:
+            captcha_post('', 0)
+
+        ip = ip_check()
+        today = get_time()
+        
+        if ban == 1:
+            return re_error('/ban')
+        
+        curs.execute("select id from topic where title = ? and sub = ? order by id + 0 desc limit 1", [name, sub])
+        old_num = curs.fetchall()
+        if old_num:
+            num = int(old_num[0][0]) + 1
+        else:
+            num = 1
+
+        match = re.search('^user:([^/]+)', name)
+        if match:
+            curs.execute('insert into alarm (name, data, date) values (?, ?, ?)', [match.groups()[0], ip + ' - <a href="/topic/' + url_pas(name) + '/sub/' + url_pas(sub) + '">' + load_lang('user_discussion', 1) + '</a>', today])
+        
+        data = re.sub('\[\[((?:분류|category):(?:(?:(?!\]\]).)*))\]\]', '[br]', flask.request.form.get('content', None))
+        for rd_data in re.findall("(?:#([0-9]+))", data):
+            curs.execute("select ip from topic where title = ? and sub = ? and id = ?", [name, sub, rd_data])
+            ip_data = curs.fetchall()
+            if ip_data and ip_or_user(ip_data[0][0]) == 0:
+                curs.execute('insert into alarm (name, data, date) values (?, ?, ?)', [ip_data[0][0], ip + ' - <a href="/topic/' + url_pas(name) + '/sub/' + url_pas(sub) + '#' + str(num) + '">' + load_lang('discussion', 1) + '</a>', today])
+            
+        data = re.sub("(?P<in>#(?:[0-9]+))", '[[\g<in>]]', data)
+
+        data = savemark(data)
+
+        rd_plus(name, sub, today)
+
+        curs.execute("insert into topic (id, title, sub, data, date, ip, block, top) values (?, ?, ?, ?, ?, ?, '', '')", [str(num), name, sub, data, today, ip])
+        conn.commit()
+        
+        return redirect('/topic/' + url_pas(name) + '/sub/' + url_pas(sub) + '#reload')
+    else:
+        curs.execute("select title from rd where title = ? and sub = ? and stop = 'O'", [name, sub])
+        close_data = curs.fetchall()
+        
+        curs.execute("select title from rd where title = ? and sub = ? and stop = 'S'", [name, sub])
+        stop_data = curs.fetchall()
+        
+        curs.execute("select id from topic where title = ? and sub = ? limit 1", [name, sub])
+        topic_exist = curs.fetchall()
+        
+        display = ''
+        all_data = ''
+        data = ''
+        number = 1
+        
+        if admin == 1 and topic_exist:
+            if close_data:
+                all_data += '<a href="/topic/' + url_pas(name) + '/sub/' + url_pas(sub) + '/tool/close">(' + load_lang('open') + ')</a> '
+            else:
+                all_data += '<a href="/topic/' + url_pas(name) + '/sub/' + url_pas(sub) + '/tool/close">(' + load_lang('close') + ')</a> '
+            
+            if stop_data:
+                all_data += '<a href="/topic/' + url_pas(name) + '/sub/' + url_pas(sub) + '/tool/stop">(' + load_lang('restart') + ')</a> '
+            else:
+                all_data += '<a href="/topic/' + url_pas(name) + '/sub/' + url_pas(sub) + '/tool/stop">(' + load_lang('stop') + ')</a> '
+
+            curs.execute("select title from rd where title = ? and sub = ? and agree = 'O'", [name, sub])
+            if curs.fetchall():
+                all_data += '<a href="/topic/' + url_pas(name) + '/sub/' + url_pas(sub) + '/tool/agree">(' + load_lang('destruction') + ')</a>'
+            else:
+                all_data += '<a href="/topic/' + url_pas(name) + '/sub/' + url_pas(sub) + '/tool/agree">(' + load_lang('agreement') + ')</a>'
+            
+            all_data += '<hr class=\"main_hr\">'
+        
+        if (close_data or stop_data) and admin != 1:
+            display = 'display: none;'
+        
+        curs.execute("select data, id, date, ip, block, top from topic where title = ? and sub = ? order by id + 0 asc", [name, sub])
+        topic = curs.fetchall()
+        
+        curs.execute("select data, id, date, ip from topic where title = ? and sub = ? and top = 'O' order by id + 0 asc", [name, sub])
+        for topic_data in curs.fetchall():                   
+            who_plus = ''
+            
+            curs.execute("select who from re_admin where what = ? order by time desc limit 1", ['notice (' + name + ' - ' + sub + '#' + topic_data[1] + ')'])
+            topic_data_top = curs.fetchall()
+            if topic_data_top:
+                who_plus += ' <span style="margin-right: 5px;">@' + topic_data_top[0][0] + ' </span>'
+                                
+            all_data += '''
+                        <table id="toron">
+                            <tbody>
+                                <tr>
+                                    <td id="toron_color_red">
+                                        <a href="#''' + topic_data[1] + '''">
+                                            #''' + topic_data[1] + '''
+                                        </a> ''' + ip_pas(topic_data[3]) + who_plus + ''' <span style="float: right;">''' + topic_data[2] + '''</span>
+                                    </td>
+                                </tr>
+                                <tr>
+                                    <td>''' + render_set(data = topic_data[0]) + '''</td>
+                                </tr>
+                            </tbody>
+                        </table>
+                        <br>
+                        '''    
+
+        for topic_data in topic:
+            user_write = topic_data[0]
+
+            if number == 1:
+                start = topic_data[3]
+
+            if topic_data[4] == 'O':
+                blind_data = 'id="toron_color_grey"'
+                
+                if admin != 1:
+                    curs.execute("select who from re_admin where what = ? order by time desc limit 1", ['blind (' + name + ' - ' + sub + '#' + str(number) + ')'])
+                    who_blind = curs.fetchall()
+                    if who_blind:
+                        user_write = '[[user:' + who_blind[0][0] + ']] ' + load_lang('hide')
+                    else:
+                        user_write = load_lang('hide')
+            else:
+                blind_data = ''
+
+            user_write = render_set(data = user_write)
+            ip = ip_pas(topic_data[3])
+            
+            curs.execute('select acl from user where id = ?', [topic_data[3]])
+            user_acl = curs.fetchall()
+            if user_acl and user_acl[0][0] != 'user':
+                ip += ' <a href="javascript:void(0);" title="' + load_lang('admin') + '">★</a>'
+
+            if admin == 1 or blind_data == '':
+                ip += ' <a href="/topic/' + url_pas(name) + '/sub/' + url_pas(sub) + '/admin/' + str(number) + '">(' + load_lang('discussion_tool') + ')</a>'
+
+            curs.execute("select end from ban where block = ?", [topic_data[3]])
+            if curs.fetchall():
+                ip += ' <a href="javascript:void(0);" title="' + load_lang('blocked') + '">†</a>'
+                    
+            if topic_data[5] == '1':
+                color = '_blue'
+            elif topic_data[3] == start:
+                color = '_green'
+            else:
+                color = ''
+                
+            if user_write == '':
+                user_write = '<br>'
+                         
+            all_data += '''
+                        <table id="toron">
+                            <tbody>
+                                <tr>
+                                    <td id="toron_color''' + color + '''">
+                                        <a href="javascript:void(0);" id="''' + str(number) + '">#' + str(number) + '</a> ' + ip + '''</span>
+                                    </td>
+                                </tr>
+                                <tr ''' + blind_data + '''>
+                                    <td>''' + user_write + '''</td>
+                                </tr>
+                            </tbody>
+                        </table>
+                        <br>
+                        '''
+            number += 1
+
+        if ban != 1 or admin == 1:
+            data += '''
+                    <div id="plus"></div>
+                    <script type="text/javascript" src="/views/main_css/topic_reload.js"></script>
+                    <script>topic_load("''' + name + '''", "''' + sub + '''");</script>
+                    <a id="reload" href="javascript:void(0);" onclick="location.href.endsWith(\'#reload\')? location.reload(true):location.href=\'#reload\'">(''' + load_lang('reload') + ''')</a>
+                    <form style="''' + display + '''" method="post">
+                    <br>
+                    <textarea style="height: 100px;" name="content"></textarea>
+                    <hr class=\"main_hr\">
+                    ''' + captcha_get()
+            
+            if display == '':
+                data += ip_warring()
+
+            data += '''
+                        <button type="submit">''' + load_lang('send') + '''</button>
+                    </form>
+                    '''
+
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [name, wiki_set(), custom(), other2([' (' + load_lang('discussion') + ')', 0])],
+            data = '<h2 id="topic_top_title">' + sub + '</h2>' + all_data + data,
+            menu = [['topic/' + url_pas(name), load_lang('list')]]
+        ))

+ 77 - 0
route/topic_admin.py

@@ -0,0 +1,77 @@
+from .tool.func import *
+
+def topic_admin_2(conn, name, sub, num):
+    curs = conn.cursor()
+
+    curs.execute("select block, ip, date from topic where title = ? and sub = ? and id = ?", [name, sub, str(num)])
+    data = curs.fetchall()
+    if not data:
+        return redirect('/topic/' + url_pas(name) + '/sub/' + url_pas(sub))
+
+    ban = ''
+
+    if admin_check(3) == 1:
+        ban +=  '''
+                </ul>
+                <br>
+                <h2>''' + load_lang('admin_tool') + '''</h2>
+                <ul>
+                '''
+        is_ban = '<li><a href="/topic/' + url_pas(name) + '/sub/' + url_pas(sub) + '/b/' + str(num) + '">'
+
+        if data[0][0] == 'O':
+            is_ban += load_lang('hide_release')
+        else:
+            is_ban += load_lang('hide')
+        
+        is_ban +=   '''
+                        </a>
+                    </li>
+                    <li>
+                        <a href="/topic/''' + url_pas(name) + '/sub/' + url_pas(sub) + '/notice/' + str(num) + '''">
+                    '''
+
+        curs.execute("select id from topic where title = ? and sub = ? and id = ? and top = 'O'", [name, sub, str(num)])
+        if curs.fetchall():
+            is_ban += load_lang('notice_release')
+        else:
+            is_ban += load_lang('notice') + ''
+        
+        is_ban += '</a></li></ul>'
+        ban += '<li><a href="/ban/' + url_pas(data[0][1]) + '">'
+
+        curs.execute("select end from ban where block = ?", [data[0][1]])
+        if curs.fetchall():
+            ban += load_lang('ban_release')
+        else:
+            ban += load_lang('ban')
+        
+        ban += '</a></li>' + is_ban
+
+    ban +=  '''
+            </ul>
+            <br>
+            <h2>''' + load_lang('other_tool') + '''</h2>
+            <ul>
+                <li>
+                    <a href="/topic/''' + url_pas(name) + '/sub/' + url_pas(sub) + '/raw/' + str(num) + '''">raw</a>
+                </li>
+            '''
+    ban = '<li>' + load_lang('time') + ' : ' + data[0][2] + '</li>' + ban
+    
+    if ip_or_user(data[0][1]) == 1:
+        ban = '<li>' + load_lang('writer') + ' : ' + data[0][1] + ' <a href="/record/' + url_pas(data[0][1]) + '">(' + load_lang('record') + ')</a></li>' + ban
+    else:
+        ban =   '''
+                <li>
+                    ''' + load_lang('writer') + ' : <a href="/w/user:' + data[0][1] + '">' + data[0][1] + '</a> <a href="/record/' + url_pas(data[0][1]) + '">(' + load_lang('record') + ''')</a>
+                </li>
+                ''' + ban
+
+    ban = '<h2>' + load_lang('state') + '</h2><ul>' + ban
+
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [load_lang('discussion_tool'), wiki_set(), custom(), other2([' (' + str(num) + ')', 0])],
+        data = ban,
+        menu = [['topic/' + url_pas(name) + '/sub/' + url_pas(sub) + '#' + str(num), load_lang('return')]]
+    ))

+ 55 - 0
route/topic_stop.py

@@ -0,0 +1,55 @@
+from .tool.func import *
+
+def topic_stop_2(conn, name, sub, tool):
+    curs = conn.cursor()
+
+    if tool == 'close':
+        set_list = [
+            'O', 
+            'S', 
+            load_lang('close', 1), 
+            load_lang('open', 1)
+        ]
+    elif tool == 'stop':
+        set_list = [
+            '', 
+            'O', 
+            load_lang('stop', 1), 
+            load_lang('restart', 1)
+        ]
+    elif tool == 'agree':
+        pass
+    else:
+        return redirect('/topic/' + url_pas(name) + '/sub/' + url_pas(sub))
+
+    if admin_check(3, 'topic ' + tool + ' (' + name + ' - ' + sub + ')') != 1:
+        return re_error('/error/3')
+
+    ip = ip_check()
+    time = get_time()
+    
+    curs.execute("select id from topic where title = ? and sub = ? order by id + 0 desc limit 1", [name, sub])
+    topic_check = curs.fetchall()
+    if topic_check:
+        if tool == 'agree':
+            curs.execute("select title from rd where title = ? and sub = ? and agree = 'O'", [name, sub])
+            if curs.fetchall():
+                curs.execute("insert into topic (id, title, sub, data, date, ip, block, top) values (?, ?, ?, '" + load_lang('agreement', 1) + " X', ?, ?, '', '1')", [str(int(topic_check[0][0]) + 1), name, sub, time, ip])
+                curs.execute("update rd set agree = '' where title = ? and sub = ?", [name, sub])
+            else:
+                curs.execute("insert into topic (id, title, sub, data, date, ip, block, top) values (?, ?, ?, '" + load_lang('agreement', 1) + " O', ?, ?, '', '1')", [str(int(topic_check[0][0]) + 1), name, sub, time, ip])
+                curs.execute("update rd set agree = 'O' where title = ? and sub = ?", [name, sub])
+        else:
+            curs.execute("select title from rd where title = ? and sub = ? and stop = ?", [name, sub, set_list[0]])
+            if curs.fetchall():
+                curs.execute("insert into topic (id, title, sub, data, date, ip, block, top) values (?, ?, ?, ?, ?, ?, '', '1')", [str(int(topic_check[0][0]) + 1), name, sub, set_list[3], time, ip])
+                curs.execute("update rd set stop = '' where title = ? and sub = ?", [name, sub])
+            else:
+                curs.execute("insert into topic (id, title, sub, data, date, ip, block, top) values (?, ?, ?, ?, ?, ?, '', '1')", [str(int(topic_check[0][0]) + 1), name, sub, set_list[2], time, ip])
+                curs.execute("update rd set stop = ? where title = ? and sub = ?", [set_list[0], name, sub])
+        
+        rd_plus(name, sub, time)
+        
+        conn.commit()
+        
+    return redirect('/topic/' + url_pas(name) + '/sub/' + url_pas(sub))    

+ 23 - 0
route/topic_top.py

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

+ 92 - 0
route/upload.py

@@ -0,0 +1,92 @@
+from .tool.func import *
+
+def upload_2(conn):
+    curs = conn.cursor()
+
+    if ban_check() == 1:
+        return re_error('/ban')
+    
+    if flask.request.method == 'POST':
+        if captcha_post(flask.request.form.get('g-recaptcha-response', '')) == 1:
+            return re_error('/error/13')
+        else:
+            captcha_post('', 0)
+
+        data = flask.request.files.get('f_data', None)
+        if not data:
+            return re_error('/error/9')
+
+        if int(wiki_set(3)) * 1024 * 1024 < flask.request.content_length:
+            return re_error('/error/17')
+        
+        value = os.path.splitext(data.filename)[1]
+        if not value in ['.jpeg', '.jpg', '.gif', '.png', '.webp', '.JPEG', '.JPG', '.GIF', '.PNG', '.WEBP']:
+            return re_error('/error/14')
+    
+        if flask.request.form.get('f_name', None):
+            name = flask.request.form.get('f_name', None) + value
+        else:
+            name = data.filename
+        
+        piece = os.path.splitext(name)
+        if re.search('[^ㄱ-힣0-9a-zA-Z_\- ]', piece[0]):
+            return re_error('/error/22')
+
+        e_data = sha224(piece[0]) + piece[1]
+
+        curs.execute("select title from data where title = ?", ['file:' + name])
+        if curs.fetchall():
+            return re_error('/error/16')
+            
+        ip = ip_check()
+
+        if flask.request.form.get('f_lice', None):
+            lice = flask.request.form.get('f_lice', None)
+        else:
+            if custom()[2] == 0:
+                lice = ip
+            else:
+                lice = '[[user:' + ip + ']]'
+            
+        if os.path.exists(os.path.join(app_var['path_data_image'], e_data)):
+            os.remove(os.path.join(app_var['path_data_image'], e_data))
+            
+            data.save(os.path.join(app_var['path_data_image'], e_data))
+        else:
+            data.save(os.path.join(app_var['path_data_image'], e_data))
+            
+        curs.execute("select title from data where title = ?", ['file:' + name])
+        if curs.fetchall(): 
+            curs.execute("delete from data where title = ?", ['file:' + name])
+        
+        curs.execute("insert into data (title, data) values (?, ?)", ['file:' + name, '[[file:' + name + ']][br][br]{{{[[file:' + name + ']]}}}[br][br]' + lice])
+        curs.execute("insert into acl (title, dec, dis, why, view) values (?, 'admin', '', '', '')", ['file:' + name])
+
+        history_plus(
+            'file:' + name, '[[file:' + name + ']][br][br]{{{[[file:' + name + ']]}}}[br][br]' + lice,
+            get_time(), 
+            ip, 
+            '(upload)',
+            '0'
+        )
+        
+        conn.commit()
+        
+        return redirect('/w/file:' + name)      
+    else:
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [load_lang('upload'), wiki_set(), custom(), other2([0, 0])],
+            data =  '''
+                    <form method="post" enctype="multipart/form-data" accept-charset="utf8">
+                        <input type="file" name="f_data">
+                        <hr class=\"main_hr\">
+                        <input placeholder="''' + load_lang('name') + '''" name="f_name" type="text">
+                        <hr class=\"main_hr\">
+                        <input placeholder="''' + load_lang('license') + '''" name="f_lice" type="text">
+                        <hr class=\"main_hr\">
+                        ''' + captcha_get() + '''
+                        <button id="save" type="submit">''' + load_lang('save') + '''</button>
+                    </form>
+                    ''',
+            menu = [['other', load_lang('return')]]
+        ))  

+ 66 - 0
route/user_admin.py

@@ -0,0 +1,66 @@
+from .tool.func import *
+
+def user_admin_2(conn, name):
+    curs = conn.cursor()
+
+    owner = admin_check()
+    
+    curs.execute("select acl from user where id = ?", [name])
+    user = curs.fetchall()
+    if not user:
+        return re_error('/error/2')
+    else:
+        if owner != 1:
+            curs.execute('select name from alist where name = ? and acl = "owner"', [user[0][0]])
+            if curs.fetchall():
+                return re_error('/error/3')
+
+            if ip_check() == name:
+                return re_error('/error/3')
+
+    if flask.request.method == 'POST':
+        if admin_check(7, 'admin (' + name + ')') != 1:
+            return re_error('/error/3')
+
+        if owner != 1:
+            curs.execute('select name from alist where name = ? and acl = "owner"', [flask.request.form.get('select', None)])
+            if curs.fetchall():
+                return re_error('/error/3')
+
+        if flask.request.form.get('select', None) == 'X':
+            curs.execute("update user set acl = 'user' where id = ?", [name])
+        else:
+            curs.execute("update user set acl = ? where id = ?", [flask.request.form.get('select', None), name])
+        
+        conn.commit()
+        
+        return redirect('/admin/' + url_pas(name))            
+    else:
+        if admin_check(7) != 1:
+            return re_error('/error/3')            
+
+        div = '<option value="X">X</option>'
+        
+        curs.execute('select distinct name from alist order by name asc')
+        for data in curs.fetchall():
+            if user[0][0] == data[0]:
+                div += '<option value="' + data[0] + '" selected="selected">' + data[0] + '</option>'
+            else:
+                if owner != 1:
+                    curs.execute('select name from alist where name = ? and acl = "owner"', [data[0]])
+                    if not curs.fetchall():
+                        div += '<option value="' + data[0] + '">' + data[0] + '</option>'
+                else:
+                    div += '<option value="' + data[0] + '">' + data[0] + '</option>'
+        
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [name, wiki_set(), custom(), other2([' (' + load_lang('authorize') + ')', 0])],
+            data =  '''
+                    <form method="post">
+                        <select name="select">''' + div + '''</select>
+                        <hr class=\"main_hr\">
+                        <button type="submit">''' + load_lang('save') + '''</button>
+                    </form>
+                    ''',
+            menu = [['manager', load_lang('return')]]
+        ))

+ 82 - 0
route/user_ban.py

@@ -0,0 +1,82 @@
+from .tool.func import *
+
+def user_ban_2(conn, name):
+    curs = conn.cursor()
+
+    if ip_or_user(name) == 0:
+        curs.execute("select acl from user where id = ?", [name])
+        user = curs.fetchall()
+        if not user:
+            return re_error('/error/2')
+
+        if user and user[0][0] != 'user':
+            if admin_check() != 1:
+                return re_error('/error/4')
+
+    if ban_check(ip = ip_check(), tool = 'login') == 1:
+        return re_error('/ban')
+                
+    if flask.request.method == 'POST':
+        if admin_check(1, 'ban (' + name + ')') != 1:
+            return re_error('/error/3')
+
+        if flask.request.form.get('limitless', '') == '':
+            end = flask.request.form.get('second', '0')
+        else:
+            end = '0'
+
+        ban_insert(name, end, flask.request.form.get('why', ''), flask.request.form.get('login', ''), ip_check())
+
+        return redirect('/ban/' + url_pas(name))     
+    else:
+        if admin_check(1) != 1:
+            return re_error('/error/3')
+
+        curs.execute("select end, why from ban where block = ?", [name])
+        end = curs.fetchall()
+        if end:
+            now = load_lang('release')
+
+            if end[0][0] == '':
+                data = '<ul><li>' + load_lang('limitless') + '</li>'
+            else:
+                data = '<ul><li>' + load_lang('period') + ' : ' + end[0][0] + '</li>'
+                
+            curs.execute("select block from ban where block = ? and login = 'O'", [name])
+            if curs.fetchall():
+                data += '<li>' + load_lang('login_able') + '</li>'
+
+            if end[0][1] != '':
+                data += '<li>' + load_lang('why') + ' : ' + end[0][1] + '</li></ul><hr class=\"main_hr\">'
+            else:
+                data += '</ul><hr class=\"main_hr\">'
+        else:
+            if re.search("^([0-9]{1,3}\.[0-9]{1,3})$", name):
+                now = load_lang('band_ban')
+            else:
+                now = load_lang('ban')
+                
+            if ip_or_user(name) == 1:
+                plus = '<input type="checkbox" name="login"> ' + load_lang('login_able') + '<hr class=\"main_hr\">'
+            else:
+                plus = ''
+
+            data =  '''
+                    <input placeholder="''' + load_lang('second') + '''" name="second" type="text">
+                    <hr class=\"main_hr\">
+                    <input type="checkbox" name="limitless"> ''' + load_lang('limitless') + '''
+                    <hr class=\"main_hr\">
+                    <input placeholder="''' + load_lang('why') + '''" name="why" type="text">
+                    <hr class=\"main_hr\">
+                    ''' + plus
+
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [name, wiki_set(), custom(), other2([' (' + now + ')', 0])],
+            data =  '''
+                    <form method="post">
+                        ''' + data + '''
+                        <button type="submit">''' + now + '''</button>
+                    </form>
+                    ''',
+            menu = [['manager', load_lang('return')]]
+        ))   

+ 92 - 0
route/user_check.py

@@ -0,0 +1,92 @@
+from .tool.func import *
+
+def user_check_2(conn, name):
+    curs = conn.cursor()
+
+    curs.execute("select acl from user where id = ? or id = ?", [name, flask.request.args.get('plus', '-')])
+    user = curs.fetchall()
+    if user and user[0][0] != 'user':
+        if admin_check() != 1:
+            return re_error('/error/4')
+
+    if admin_check(4, 'check (' + name + ')') != 1:
+        return re_error('/error/3')
+        
+    num = int(number_check(flask.request.args.get('num', '1')))
+    if num * 50 > 0:
+        sql_num = num * 50 - 50
+    else:
+        sql_num = 0
+    
+    if flask.request.args.get('plus', None):
+        end_check = 1
+    
+        if ip_or_user(name) == 1:
+            if ip_or_user(flask.request.args.get('plus', None)) == 1:
+                curs.execute("select name, ip, ua, today from ua_d where ip = ? or ip = ? order by today desc limit ?, '50'", [name, flask.request.args.get('plus', None), sql_num])
+            else:
+                curs.execute("select name, ip, ua, today from ua_d where ip = ? or name = ? order by today desc limit ?, '50'", [name, flask.request.args.get('plus', None), sql_num])
+        else:
+            if ip_or_user(flask.request.args.get('plus', None)) == 1:
+                curs.execute("select name, ip, ua, today from ua_d where name = ? or ip = ? order by today desc limit ?, '50'", [name, flask.request.args.get('plus', None), sql_num])
+            else:
+                curs.execute("select name, ip, ua, today from ua_d where name = ? or name = ? order by today desc limit ?, '50'", [name, flask.request.args.get('plus', None), sql_num])
+    else:
+        end_check = 0
+        
+        if ip_or_user(name) == 1:
+            curs.execute("select name, ip, ua, today from ua_d where ip = ? order by today desc limit ?, '50'", [name, sql_num])
+        else:
+            curs.execute("select name, ip, ua, today from ua_d where name = ? order by today desc limit ?, '50'", [name, sql_num])
+    
+    record = curs.fetchall()
+    if record:
+        if not flask.request.args.get('plus', None):
+            div = '<a href="/manager/14?plus=' + url_pas(name) + '">(' + load_lang('compare') + ')</a><hr class=\"main_hr\">'
+        else:
+            div = '<a href="/check/' + url_pas(name) + '">(' + name + ')</a> <a href="/check/' + url_pas(flask.request.args.get('plus', None)) + '">(' + flask.request.args.get('plus', None) + ')</a><hr class=\"main_hr\">'
+
+        div +=  '''
+                <table id="main_table_set">
+                    <tbody>
+                        <tr>
+                            <td id="main_table_width">''' + load_lang('name') + '''</td>
+                            <td id="main_table_width">ip</td>
+                            <td id="main_table_width">''' + load_lang('time') + '''</td>
+                        </tr>
+                '''
+        
+        for data in record:
+            if data[2]:
+                ua = data[2]
+            else:
+                ua = '<br>'
+
+            div +=  '''
+                    <tr>
+                        <td>''' + ip_pas(data[0]) + '''</td>
+                        <td>''' + ip_pas(data[1]) + '''</td>
+                        <td>''' + data[3] + '''</td>
+                    </tr>
+                    <tr>
+                        <td colspan="3">''' + ua + '''</td>
+                    </tr>
+                    '''
+        
+        div +=  '''
+                    </tbody>
+                </table>
+                '''
+    else:
+        return re_error('/error/2')
+        
+    if end_check == 1:
+        div += next_fix('/check/' + url_pas(name) + '?plus=' + flask.request.args.get('plus', None) + '&num=', num, record)
+    else:
+        div += next_fix('/check/' + url_pas(name) + '?num=', num, record)
+            
+    return easy_minify(flask.render_template(skin_check(),    
+        imp = [load_lang('check'), wiki_set(), custom(), other2([0, 0])],
+        data = div,
+        menu = [['manager', load_lang('return')]]
+    ))

+ 101 - 0
route/user_info.py

@@ -0,0 +1,101 @@
+from .tool.func import *
+
+def user_info_2(conn):
+    curs = conn.cursor()
+
+    ip = ip_check()
+    
+    curs.execute("select acl from user where id = ?", [ip])
+    data = curs.fetchall()
+    if ban_check() == 0:
+        if data:
+            if data[0][0] != 'user':
+                acl = data[0][0]
+            else:
+                acl = load_lang('member')
+        else:
+            acl = load_lang('normal')
+    else:
+        acl = load_lang('blocked')
+
+        match = re.search("^([0-9]{1,3}\.[0-9]{1,3})", ip)
+        if match:
+            match = match.groups()[0]
+        else:
+            match = '-'
+
+        curs.execute("select end, login, band from ban where block = ? or block = ?", [ip, match])
+        block_data = curs.fetchall()
+        if block_data:
+            if block_data[0][0] != '':
+                acl += ' (' + load_lang('period') + ' : ' + block_data[0][0] + ')'
+            else:
+                acl += ' (' + load_lang('limitless') + ')'        
+
+            if block_data[0][1] != '':
+                acl += ' (' + load_lang('login_able') + ')'
+
+            if block_data[0][2] == 'O':
+                acl += ' (' + load_lang('band_blocked') + ')'
+            
+    if custom()[2] != 0:
+        ip_user = '<a href="/w/user:' + ip + '">' + ip + '</a>'
+        
+        plus =  '''
+                <li><a href="/logout">''' + load_lang('logout') + '''</a></li>
+                <li><a href="/change">''' + load_lang('user_setting') + '''</a></li>
+                '''
+        
+        curs.execute('select name from alarm where name = ? limit 1', [ip_check()])
+        if curs.fetchall():
+            plus2 = '<li><a href="/alarm">' + load_lang('alarm') + ' (O)</a></li>'
+        else:
+            plus2 = '<li><a href="/alarm">' + load_lang('alarm') + '</a></li>'
+
+        plus2 += '<li><a href="/watch_list">' + load_lang('watchlist') + '</a></li>'
+        plus3 = '<li><a href="/acl/user:' + url_pas(ip) + '">' + load_lang('user_document_acl') + '</a></li>'
+    else:
+        ip_user = ip
+        
+        plus =  '''
+                <li><a href="/login">''' + load_lang('login') + '''</a></li>
+                <li><a href="/register">''' + load_lang('register') + '''</a></li>
+                '''
+        plus2 = ''
+        plus3 = ''
+
+        curs.execute("select data from other where name = 'email_have'")
+        test = curs.fetchall()
+        if test and test[0][0] != '':
+            plus += '<li><a href="/pass_find">' + load_lang('password_search') + '</a></li>'
+
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [load_lang('user') + ' ' + load_lang('tool'), wiki_set(), custom(), other2([0, 0])],
+        data =  '''
+                <h2>''' + load_lang('state') + '''</h2>
+                <ul>
+                    <li>''' + ip_user + ''' <a href="/record/''' + url_pas(ip) + '''">(''' + load_lang('record') + ''')</a></li>
+                    <li>''' + load_lang('state') + ''' : ''' + acl + '''</li>
+                </ul>
+                <br>
+                <h2>''' + load_lang('login') + '''</h2>
+                <ul>
+                    ''' + plus + '''
+                </ul>
+                <br>
+                <h2>''' + load_lang('tool') + '''</h2>
+                <ul>
+                    ''' + plus3 + '''
+                    <li><a href="/custom_head">''' + load_lang('user_head') + '''</a></li>
+                </ul>
+                <br>
+                <h2>''' + load_lang('other') + '''</h2>
+                <ul>
+                ''' + plus2 + '''
+                <li>
+                    <a href="/count">''' + load_lang('count') + '''</a>
+                </li>
+                </ul>
+                ''',
+        menu = 0
+    ))

+ 57 - 0
route/user_log.py

@@ -0,0 +1,57 @@
+from .tool.func import *
+
+def user_log_2(conn):
+    curs = conn.cursor()
+
+    num = int(number_check(flask.request.args.get('num', '1')))
+    if num * 50 > 0:
+        sql_num = num * 50 - 50
+    else:
+        sql_num = 0
+        
+    list_data = '<ul>'
+
+    admin_one = admin_check(1)
+    
+    curs.execute("select id, date from user order by date desc limit ?, '50'", [str(sql_num)])
+    user_list = curs.fetchall()
+    for data in user_list:
+        if admin_one == 1:
+            curs.execute("select block from ban where block = ?", [data[0]])
+            if curs.fetchall():
+                ban_button = ' <a href="/ban/' + url_pas(data[0]) + '">(' + load_lang('ban_release') + ')</a>'
+            else:
+                ban_button = ' <a href="/ban/' + url_pas(data[0]) + '">(' + load_lang('ban') + ')</a>'
+        else:
+            ban_button = ''
+            
+        list_data += '<li>' + ip_pas(data[0]) + ban_button
+        
+        if data[1] != '':
+            list_data += ' (' + data[1] + ')'
+
+        list_data += '</li>'
+
+    if num == 1:
+        curs.execute("select count(id) from user")
+        user_count = curs.fetchall()
+        if user_count:
+            count = user_count[0][0]
+        else:
+            count = 0
+
+        list_data +=    '''
+            </ul>
+            <hr class=\"main_hr\">
+            <ul>
+                <li>all : ''' + str(count) + '''</li>
+            </ul>
+        '''
+
+    list_data += next_fix('/user_log?num=', num, user_list)
+
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [load_lang('member_list'), wiki_set(), custom(), other2([0, 0])],
+        data = list_data,
+        menu = [['other', load_lang('return')]]
+    ))

+ 32 - 0
route/user_tool.py

@@ -0,0 +1,32 @@
+from .tool.func import *
+
+def user_tool_2(conn, name):
+    curs = conn.cursor()
+    
+    data =  '''
+            <h2>''' + load_lang('tool') + '''</h2>
+            <ul>
+                <li><a href="/record/''' + url_pas(name) + '''">''' + load_lang('record') + '''</a></li>
+            </ul>
+            '''
+            
+    if admin_check(1) == 1:
+        curs.execute("select block from ban where block = ?", [name])
+        if curs.fetchall():
+            ban_name = load_lang('ban_release')
+        else:
+            ban_name = load_lang('ban')
+    
+        data += '''
+                <h2>''' + load_lang('admin') + '''</h2>
+                <ul>
+                    <li><a href="/ban/''' + url_pas(name) + '''">''' + ban_name + '''</a></li>
+                    <li><a href="/check/''' + url_pas(name) + '''">''' + load_lang('check') + '''</a></li>
+                </ul>
+                '''
+
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [name, wiki_set(), custom(), other2([' (' + load_lang('tool') + ')', 0])],
+        data = data,
+        menu = 0
+    ))

+ 55 - 0
route/user_topic_list.py

@@ -0,0 +1,55 @@
+from .tool.func import *
+
+def user_topic_list_2(conn, name):
+    curs = conn.cursor()
+
+    num = int(number_check(flask.request.args.get('num', '1')))
+    if num * 50 > 0:
+        sql_num = num * 50 - 50
+    else:
+        sql_num = 0
+    
+    one_admin = admin_check(1)
+
+    div =   '''
+            <table id="main_table_set">
+                <tbody>
+                    <tr>
+                        <td id="main_table_width">''' + load_lang('discussion_name') + '''</td>
+                        <td id="main_table_width">''' + load_lang('writer') + '''</td>
+                        <td id="main_table_width">''' + load_lang('time') + '''</td>
+                    </tr>
+            '''
+    
+    curs.execute("select title, id, sub, ip, date from topic where ip = ? order by date desc limit ?, '50'", [name, str(sql_num)])
+    data_list = curs.fetchall()
+    for data in data_list:
+        title = html.escape(data[0])
+        sub = html.escape(data[2])
+        
+        if one_admin == 1:
+            curs.execute("select * from ban where block = ?", [data[3]])
+            if curs.fetchall():
+                ban = ' <a href="/ban/' + url_pas(data[3]) + '">(' + load_lang('release') + ')</a>'
+            else:
+                ban = ' <a href="/ban/' + url_pas(data[3]) + '">(' + load_lang('ban') + ')</a>'
+        else:
+            ban = ''
+            
+        div += '<tr><td><a href="/topic/' + url_pas(data[0]) + '/sub/' + url_pas(data[2]) + '#' + data[1] + '">' + title + '#' + data[1] + '</a> (' + sub + ')</td>'
+        div += '<td>' + ip_pas(data[3]) + ban + '</td><td>' + data[4] + '</td></tr>'
+
+    div += '</tbody></table>'
+    div += next_fix('/topic_record/' + url_pas(name) + '?num=', num, data_list)      
+    
+    curs.execute("select end from ban where block = ?", [name])
+    if curs.fetchall():
+        sub = ' (' + load_lang('blocked') + ')'
+    else:
+        sub = 0 
+    
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [load_lang('discussion_record'), wiki_set(), custom(), other2([sub, 0])],
+        data = div,
+        menu = [['other', load_lang('other')], ['user', load_lang('user')], ['count/' + url_pas(name), load_lang('count')], ['record/' + url_pas(name), load_lang('record')]]
+    ))

+ 25 - 0
route/watch_list.py

@@ -0,0 +1,25 @@
+from .tool.func import *
+
+def watch_list_2(conn):
+    curs = conn.cursor()
+
+    div = 'limit : 10<hr class=\"main_hr\">'
+    
+    if custom()[2] == 0:
+        return redirect('/login')
+
+    curs.execute("select title from scan where user = ?", [ip_check()])
+    data = curs.fetchall()
+    for data_list in data:
+        div += '<li><a href="/w/' + url_pas(data_list[0]) + '">' + data_list[0] + '</a> <a href="/watch_list/' + url_pas(data_list[0]) + '">(' + load_lang('delete') + ')</a></li>'
+
+    if data:
+        div = '<ul>' + div + '</ul><hr class=\"main_hr\">'
+
+    div += '<a href="/manager/13">(' + load_lang('add') + ')</a>'
+
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [load_lang('watchlist'), wiki_set(), custom(), other2([0, 0])],
+        data = div,
+        menu = [['manager', load_lang('return')]]
+    ))

+ 24 - 0
route/watch_list_name.py

@@ -0,0 +1,24 @@
+from .tool.func import *
+
+def watch_list_name_2(conn, name):
+    curs = conn.cursor()
+    
+    if custom()[2] == 0:
+        return redirect('/login')
+
+    ip = ip_check()
+
+    curs.execute("select count(title) from scan where user = ?", [ip])
+    count = curs.fetchall()
+    if count and count[0][0] > 9:
+        return redirect('/watch_list')
+
+    curs.execute("select title from scan where user = ? and title = ?", [ip, name])
+    if curs.fetchall():
+        curs.execute("delete from scan where user = ? and title = ?", [ip, name])
+    else:
+        curs.execute("insert into scan (user, title) values (?, ?)", [ip, name])
+    
+    conn.commit()
+
+    return redirect('/watch_list')

+ 35 - 0
route/xref.py

@@ -0,0 +1,35 @@
+from .tool.func import *
+
+def xref_2(conn, name):
+    curs = conn.cursor()
+
+    num = int(number_check(flask.request.args.get('num', '1')))
+    if num * 50 > 0:
+        sql_num = num * 50 - 50
+    else:
+        sql_num = 0
+        
+    div = '<ul>'
+    
+    curs.execute("select link, type from back where title = ? and not type = 'cat' and not type = 'no' order by link asc limit ?, '50'", [name, str(sql_num)])
+    data_list = curs.fetchall()
+    for data in data_list:
+        div += '<li><a href="/w/' + url_pas(data[0]) + '">' + data[0] + '</a>'
+        
+        if data[1]:                
+            div += ' (' + data[1] + ')'
+        
+        curs.execute("select title from back where title = ? and type = 'include'", [data[0]])
+        db_data = curs.fetchall()
+        if db_data:
+            div += ' <a id="inside" href="/xref/' + url_pas(data[0]) + '">(' + load_lang('backlink') + ')</a>'
+
+        div += '</li>'
+      
+    div += '</ul>' + next_fix('/xref/' + url_pas(name) + '?num=', num, data_list)
+    
+    return easy_minify(flask.render_template(skin_check(), 
+        imp = [name, wiki_set(), custom(), other2([' (' + load_lang('backlink') + ')', 0])],
+        data = div,
+        menu = [['w/' + url_pas(name), load_lang('return')]]
+    ))

+ 0 - 6
views/easter_egg.html

@@ -1,6 +0,0 @@
-<iframe width="560" height="315" src="https://www.youtube.com/embed/O6BJiije6m4" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
-<br>
-<br>
-<div>
-    노라조와 영접하십셔.
-</div>

+ 2 - 1
views/main_css/main.css

@@ -31,4 +31,5 @@ s:hover, strike:hover, del:hover { color: gray; background-color: gainsboro; tex
 #main_table_width_half { width: 50%; }
 #main_table_width_half { width: 50%; }
 #main_table_width_quarter { width: 25%; }
 #main_table_width_quarter { width: 25%; }
 #redirect { border: 1px solid; padding: 10px; }
 #redirect { border: 1px solid; padding: 10px; }
-body { word-break: break-all; overflow: scroll; }
+body { word-break: break-all; overflow: scroll; }
+hr.main_hr { border: none; }

+ 56 - 0
views/main_css/oauth.css

@@ -0,0 +1,56 @@
+.oauth-wrapper {
+    display: inline-block;
+}
+
+.oauth-list {
+    list-style: none;
+    padding: 0;
+}
+
+.oauth-list li {
+    display: inline-block;
+}
+
+.oauth-btn {
+    position: relative;
+    margin-left: 20px;
+    border: none;
+    display: inline-block;
+    padding: 25px;
+    padding-left: 75px;
+    border-radius: 10px;
+    color: #fff;
+}
+
+.oauth-btn a {
+    position: relative;
+}
+
+.oauth-btn.oauth-btn-facebook {
+    background-color: #3b5998;
+}
+
+.oauth-btn.oauth-btn-naver {
+    background-color: #00d337;
+}
+
+.oauth-btn-logo {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 10px;
+    height: 10px;
+    margin: 20px;
+    margin-left: 25px;
+    padding: 10px;
+    background-size: cover;
+    background-position: center center;
+}
+
+.oauth-btn-logo.oauth-btn-facebook {
+    background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBmaWxsPSIjZmZmIiBkPSJNOSA4aC0zdjRoM3YxMmg1di0xMmgzLjY0MmwuMzU4LTRoLTR2LTEuNjY3YzAtLjk1NS4xOTItMS4zMzMgMS4xMTUtMS4zMzNoMi44ODV2LTVoLTMuODA4Yy0zLjU5NiAwLTUuMTkyIDEuNTgzLTUuMTkyIDQuNjE1djMuMzg1eiIvPjwvc3ZnPg==);
+}
+
+.oauth-btn-logo.oauth-btn-naver {
+    background-image : url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNTYiIGhlaWdodD0iMjU2IiB2aWV3Qm94PSIwIDAgMjU2IDI1NiI+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTEzLDI3VjIyOS45NzJIOTEuNTRWMTI2LjcwNmw3NC44NDQsMTAzLjI4NUwyNDQsMjI5Ljk3MlYyNy4wMDlIMTY2LjM4NFYxMjguNDg2TDkyLjQ2NCwyNy4wMDlaIi8+PC9zdmc+)
+}

+ 1 - 1
views/main_css/topic_reload.js

@@ -27,7 +27,7 @@ function topic_load(name, sub) {
                     console.log(xhr.responseText);
                     console.log(xhr.responseText);
                     console.log(url);
                     console.log(url);
 
 
-                    doc_data.innerText += '(new topic)\n\n';
+                    doc_data.innerText += '(New)\n\n';
 
 
                     clearInterval(test);
                     clearInterval(test);
                 }
                 }

+ 14 - 7
views/neo_yousoro/css/main.css

@@ -3,7 +3,8 @@ body {
     word-break: break-all;
     word-break: break-all;
     word-wrap: break-word;
     word-wrap: break-word;
     margin: 0;
     margin: 0;
-    font-family: '나눔고딕', 'nanumgothic', "Nanum Gothic","KoPub Dotum","Malgun Gothic","맑은 고딕",sans-serif;
+    font-family: "나눔고딕", "nanumgothic", "Nanum Gothic", "KoPub Dotum", "Malgun Gothic", "맑은 고딕", "sans-serif";
+    overflow: auto;
 }
 }
 
 
 ul {
 ul {
@@ -50,8 +51,8 @@ li {
 #top {
 #top {
     width: 100%;
     width: 100%;
     border-bottom: 2px solid gainsboro;
     border-bottom: 2px solid gainsboro;
-    background: #fff;
-    padding: 10px;
+    background: skyblue;
+    padding: 15px;
     padding-right: 0;
     padding-right: 0;
     padding-left: 0;
     padding-left: 0;
     position: relative;
     position: relative;
@@ -68,6 +69,7 @@ li {
     background: white;
     background: white;
     border-left: 2px solid gainsboro;
     border-left: 2px solid gainsboro;
     border-right: 2px solid gainsboro;
     border-right: 2px solid gainsboro;
+    min-height: 350px;
 }
 }
 
 
 #bottom {
 #bottom {
@@ -274,6 +276,7 @@ h6 {
     right: 0;
     right: 0;
     background: white;
     background: white;
     border-left: 2px solid gainsboro;
     border-left: 2px solid gainsboro;
+    z-index: 1;
 }
 }
 
 
 #mobile_menu_main {
 #mobile_menu_main {
@@ -338,14 +341,14 @@ input {
 #go_toc {
 #go_toc {
     display: inline-block;
     display: inline-block;
     padding: 10px;
     padding: 10px;
-    border-left: 2px solid white;
+    border-left: 2px solid gainsboro;
     width: 25px;
     width: 25px;
 }
 }
 
 
 #go_top {
 #go_top {
     display: inline-block;
     display: inline-block;
     padding: 10px;
     padding: 10px;
-    border-right: 2px solid white;
+    border-right: 2px solid gainsboro;
     width: 25px;
     width: 25px;
 }
 }
 
 
@@ -361,7 +364,11 @@ input {
     position: fixed;
     position: fixed;
     bottom: 0;
     bottom: 0;
     right: 0;
     right: 0;
-    border: 2px solid white;
-    background: gainsboro;
+    border: 2px solid gainsboro;
+    background: skyblue;
     text-align: center;
     text-align: center;
+}
+
+#nav_bar a {
+    color: black;
 }
 }

+ 11 - 6
views/neo_yousoro/index.html

@@ -1,3 +1,4 @@
+<!DOCTYPE html>
 <html>
 <html>
     <head>
     <head>
         <meta charset="utf-8">
         <meta charset="utf-8">
@@ -5,9 +6,10 @@
         <link rel="stylesheet" href="/views/main_css/main.css">
         <link rel="stylesheet" href="/views/main_css/main.css">
         <link rel="stylesheet" href="/views/neo_yousoro/css/main.css">
         <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/main.js"></script>
+        <script src="/views/neo_yousoro/js/skin_set.js"></script>
         <link   rel="stylesheet"
         <link   rel="stylesheet"
-                href="https://use.fontawesome.com/releases/v5.2.0/css/all.css"
-                integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ"
+                href="https://use.fontawesome.com/releases/v5.7.2/css/all.css"
+                integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr"
                 crossorigin="anonymous">
                 crossorigin="anonymous">
         <link rel="shortcut icon" href="/views/main_css/file/favicon.ico">
         <link rel="shortcut icon" href="/views/main_css/file/favicon.ico">
         {{imp[1][5]|safe}}
         {{imp[1][5]|safe}}
@@ -31,7 +33,7 @@
                             <div id="recent_cel" class="cel_in_cel" style="display: none;">
                             <div id="recent_cel" class="cel_in_cel" style="display: none;">
                                 <a href="/recent_changes">
                                 <a href="/recent_changes">
                                     <i class="fas fa-sync-alt"></i>
                                     <i class="fas fa-sync-alt"></i>
-                                    {{'change'|load_lang}}
+                                    {{'edit'|load_lang}}
                                 </a>
                                 </a>
                                 <hr>
                                 <hr>
                                 <a href="/recent_discuss">
                                 <a href="/recent_discuss">
@@ -60,7 +62,7 @@
                                 <hr>
                                 <hr>
                                 <a href="/skin_set">
                                 <a href="/skin_set">
                                     <i class="fas fa-cog"></i>
                                     <i class="fas fa-cog"></i>
-                                    {{'skin'|load_lang}} {{'setting'|load_lang}}
+                                    {{'skin_setting'|load_lang}}
                                 </a>
                                 </a>
                             </div>
                             </div>
                         </div>
                         </div>
@@ -108,12 +110,12 @@
                         </a>
                         </a>
                     </div>
                     </div>
                     <h2>{{'recent'|load_lang}}</h2>
                     <h2>{{'recent'|load_lang}}</h2>
-                    <li><a href="/recent_changes">{{'change'|load_lang}}</a></li>
+                    <li><a href="/recent_changes">{{'edit'|load_lang}}</a></li>
                     <li><a href="/recent_discuss">{{'discussion'|load_lang}}</a></li>
                     <li><a href="/recent_discuss">{{'discussion'|load_lang}}</a></li>
                     <h2>{{'other'|load_lang}}</h2>
                     <h2>{{'other'|load_lang}}</h2>
                     <li><a href="/random">{{'random'|load_lang}}</a></li>
                     <li><a href="/random">{{'random'|load_lang}}</a></li>
                     <li><a href="/other">{{'tool'|load_lang}}</a></li>
                     <li><a href="/other">{{'tool'|load_lang}}</a></li>
-                    <li><a href="/skin_set">{{'skin'|load_lang}} {{'setting'|load_lang}}</a></li>
+                    <li><a href="/skin_set">{{'skin_setting'|load_lang}}</a></li>
                     <h2>{{'user'|load_lang}}</h2>
                     <h2>{{'user'|load_lang}}</h2>
                     <li><a href="/user">{{imp[2][5]}}</a></li>
                     <li><a href="/user">{{imp[2][5]}}</a></li>
                 </div>
                 </div>
@@ -159,6 +161,9 @@
         <div id="bottom">
         <div id="bottom">
             <div id="bottom_main">
             <div id="bottom_main">
                 {{imp[1][1]|safe}}
                 {{imp[1][1]|safe}}
+                <br>
+                <br>
+                <a href="https://github.com/2du/openNAMU"><img id="b_logo" src="/views/main_css/file/s_logo.png"></a>
             </div>
             </div>
         </div>
         </div>
         <div id="nav_bar">
         <div id="nav_bar">

+ 0 - 70
views/neo_yousoro/js/main.js

@@ -1,37 +1,3 @@
-// 쿠키 생성
-function setCookie(name, value, expiredays) {
-    var cookie = name + "=" + escape(value) + "; path=/;"
-    if (typeof expiredays != 'undefined') {
-        var todayDate = new Date();
-        todayDate.setDate(todayDate.getDate() + expiredays);
-        cookie += "expires=" + todayDate.toGMTString() + ";"
-    }
-    document.cookie = cookie;
-}
- 
-// 쿠키 획득
-function getCookie(name) {
-    name += "=";
-    var cookie = document.cookie;
-    var startIdx = cookie.indexOf(name);
-    if (startIdx != -1) {
-        startIdx += name.length;
-        var endIdx = cookie.indexOf(";", startIdx);
-        if (endIdx == -1) {
-            endIdx = cookie.length;
-            return unescape(cookie.substring(startIdx, endIdx));
-        }
-    }
-    return null;
-}
- 
-// 쿠키 삭제
-function deleteCookie(name) {
-    setCookie(name, "", -1);
-}
-
-// http://vip00112.tistory.com/33
-
 function opening(data) {
 function opening(data) {
     var element = document.getElementById(data);
     var element = document.getElementById(data);
     if(element.style.display == 'none') {
     if(element.style.display == 'none') {
@@ -39,40 +5,4 @@ function opening(data) {
     } else {
     } else {
         element.style.display = 'none';
         element.style.display = 'none';
     }
     }
-}
-
-function get_post() {
-    check = document.getElementById('strike');
-    if(check.checked == true) {
-        setCookie("set_strike", "1");
-    } else {
-        deleteCookie("set_strike");
-    }
-    
-    window.location.reload(true);
-}
-
-head_data = document.querySelector('head');
-if(getCookie("set_strike") == "1") {
-    head_data.innerHTML += '<style>s { display: none; }';
-}
-
-window.onload = function () {
-    if(window.location.pathname == '/skin_set') {
-        document.getElementById("main_top").innerHTML = '<h1>skin setting</h1>';
-        data = document.getElementById("main_data")
-        
-        set_data = {};
-        if(getCookie("set_strike") == "1") {
-            set_data["strike"] = "checked";
-        } 
-        
-        data.innerHTML =    `
-                            <input ` + set_data["strike"] + ` type="checkbox" id="strike" name="strike" value="strike"> remove strikethrough
-                            <hr>
-                            <button onclick="get_post();">Save</button>
-                            `;
-                            
-        document.title = document.title.replace(/.*(\- .*)$/, "skin setting $1");
-    }
 }
 }

+ 69 - 0
views/neo_yousoro/js/skin_set.js

@@ -0,0 +1,69 @@
+// 쿠키 생성
+function setCookie(name, value, expiredays) {
+    var cookie = name + "=" + escape(value) + "; path=/;"
+    if (typeof expiredays != 'undefined') {
+        var todayDate = new Date();
+        todayDate.setDate(todayDate.getDate() + expiredays);
+        cookie += "expires=" + todayDate.toGMTString() + ";"
+    }
+    document.cookie = cookie;
+}
+ 
+// 쿠키 획득
+function getCookie(name) {
+    name += "=";
+    var cookie = document.cookie;
+    var startIdx = cookie.indexOf(name);
+    if (startIdx != -1) {
+        startIdx += name.length;
+        var endIdx = cookie.indexOf(";", startIdx);
+        if (endIdx == -1) {
+            endIdx = cookie.length;
+            return unescape(cookie.substring(startIdx, endIdx));
+        }
+    }
+    return null;
+}
+ 
+// 쿠키 삭제
+function deleteCookie(name) {
+    setCookie(name, "", -1);
+}
+
+// http://vip00112.tistory.com/33
+
+function get_post() {
+    check = document.getElementById('strike');
+    if(check.checked == true) {
+        setCookie("set_strike", "1");
+    } else {
+        deleteCookie("set_strike");
+    }
+    
+    window.location.reload(true);
+}
+
+head_data = document.querySelector('head');
+if(getCookie("set_strike") == "1") {
+    head_data.innerHTML += '<style>s { display: none; }';
+}
+
+window.onload = function () {
+    if(window.location.pathname == '/skin_set') {
+        document.getElementById("main_top").innerHTML = '<h1>Skin setting</h1>';
+        data = document.getElementById("main_data")
+        
+        set_data = {};
+        if(getCookie("set_strike") == "1") {
+            set_data["strike"] = "checked";
+        } 
+        
+        data.innerHTML = ' \
+            <input ' + set_data["strike"] + ' type="checkbox" id="strike" name="strike" value="strike"> Remove strikethrough \
+            <hr> \
+            <button onclick="get_post();">Save</button> \
+        ';
+                            
+        document.title = document.title.replace(/.*(\- .*)$/, "skin setting $1");
+    }
+}

Some files were not shown because too many files changed in this diff