Bladeren bron

Merge pull request #679 from 2du/master

새 버전
잉여개발기 (SPDV) 7 jaren geleden
bovenliggende
commit
b284edeea5

+ 1 - 1
LICENSE

@@ -1,6 +1,6 @@
 BSD 3-Clause License
 
-Copyright (c) 2017-2018, 2DU
+Copyright (c) 2017-2019, 2DU
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without

+ 14 - 9
app.py

@@ -389,11 +389,12 @@ def search():
     return search_2(conn)
 
 @app.route('/goto', methods=['POST'])
-def search_goto():
-    return search_goto_2(conn)
+@app.route('/goto/<everything:name>', methods=['POST'])
+def search_goto(name = 'test'):
+    return search_goto_2(conn, name)
 
 @app.route('/search/<everything:name>')
-def search_deep(name = ''):
+def search_deep(name = 'test'):
     return search_deep_2(conn, name)
          
 @app.route('/raw/<everything:name>')
@@ -419,12 +420,12 @@ def edit_move(name = None):
 
 @app.route('/other')
 def main_other():
-    return main_other_2(conn, r_ver)
+    return main_other_2(conn)
     
 @app.route('/manager', methods=['POST', 'GET'])
 @app.route('/manager/<int:num>', methods=['POST', 'GET'])
 def main_manager(num = 1):
-    return main_manager_2(conn, num)
+    return main_manager_2(conn, num, r_ver)
         
 @app.route('/title_index')
 def list_title_index():
@@ -472,8 +473,12 @@ def login_oauth(platform = None, func = None):
     return login_oauth_2(conn, platform, func)
                 
 @app.route('/change', methods=['POST', 'GET'])
-def login_change_password():
-    return login_change_password_2(conn, server_init)
+def user_setting():
+    return user_setting_2(conn, server_init)
+    
+@app.route('/pw_change', methods=['POST', 'GET'])
+def login_pw_change():
+    return login_pw_change_2(conn)
 
 @app.route('/check/<name>')
 def give_user_check(name = None):
@@ -483,11 +488,11 @@ def give_user_check(name = None):
 def login_register():
     return login_register_2(conn)
 
-@app.route('/<regex("need_email|pass_find"):tool>', methods=['POST', 'GET'])
+@app.route('/<regex("need_email|pass_find|email_change"):tool>', methods=['POST', 'GET'])
 def login_need_email(tool = 'pass_find'):
     return login_need_email_2(conn, tool)
 
-@app.route('/<regex("check_key|check_pass_key"):tool>', methods=['POST', 'GET'])
+@app.route('/<regex("check_key|check_pass_key|email_replace"):tool>', methods=['POST', 'GET'])
 def login_check_key(tool = 'check_pass_key'):
     return login_check_key_2(conn, tool)
            

+ 14 - 9
emergency_tool.py

@@ -1,8 +1,3 @@
-import os
-import sqlite3
-import hashlib
-import threading
-
 from route.tool.func import *
 from route.tool.mark import load_conn2, namumark
 
@@ -78,7 +73,14 @@ elif what_i_do == '3':
     else:
         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("insert into rb (block, end, today, blocker, why, band) values (?, ?, ?, ?, ?, ?)", 
+            [user_data, 
+            'release', 
+            get_time(), 
+            'tool:emergency', 
+            '', 
+            band
+        ])
     curs.execute("delete from ban where block = ?", [user_data])
 elif what_i_do == '4':
     print('Host : ', end = '')
@@ -109,13 +111,16 @@ elif what_i_do == '7':
 
     if what_i_do == '1':
         hashed = hashlib.sha256(bytes(user_pw, 'utf-8')).hexdigest()
-    elif what_i_do == '2':
-        hashed = sha3_256(bytes(user_pw, 'utf-8')).hexdigest()
+    else:
+        if sys.version_info < (3, 6):
+            hashed = sha3.sha3_256(bytes(user_pw, 'utf-8')).hexdigest()
+        else:
+            hashed = hashlib.sha3_256(bytes(user_pw, 'utf-8')).hexdigest()
        
     curs.execute("update user set pw = ? where id = ?", [hashed, user_name])
 elif what_i_do == '8':
     curs.execute("update other set data = '00000' where name = 'ver'")
-elif what_i_do == '9':
+else:
     print('DB\'s name (data) : ', end = '')
     
     db_name = input()

+ 41 - 19
language/en-US.json

@@ -152,30 +152,17 @@
         "user_document" : "User[s] document",
         "user_head" : "User[s] <head>",
         "user_document_acl" : "User[s] document ACL",
-        "admin_acl" : "Admin only",
-        "member_acl" : "Member only",
-        "50_edit_acl" : "Only members with 50 or more all document edits",
-        "all_acl" : "All Users",
         "encryption_method" : "Encryption method",
-        "check_key" : "Check Authentication Key",
+        "check_key" : "Check authentication key",
         "reset_user_ok" : "Check Success",
         "name_or_ip_or_regex" : "ID or IP or Regex",
         "ban_period" : "Period to block",
         "not_sure" : "Not sure",
         "use_push_alarm" : "Enable Push Notification",
-        "topic_tool" : "Discussion tool",
-        "topic_state" : "Discussion status",
-        "topic_stop" : "Stop discussion",
-        "topic_close" : "Close discussion",
-        "topic_restart" : "Discussion restart",
-        "topic_open" : "Open discussion",
-        "topic_agreement" : "Discussion agreement",
-        "topic_destruction" : "Cancel discussion agreement",
-        "1_day" : "1 day",
-        "5_day" : "5 days",
-        "30_day" : "30 days",
-        "180_day" : "180 days",
-        "360_day" : "360 days",
+        "edit_button_paragraph" : "Paragraph",
+        "password_change" : "Password change",
+        "email_change" : "Email change",
+        "acl_change" : "Change document ACL",
         "_comment_2.1_" : "Filter",
             "_comment_2.1.1_" : "List",
                 "interwiki_list" : "Interwiki(s) list",
@@ -223,6 +210,12 @@
                 "register_text" : "Terms of sign-up",
                 "non_login_alert" : "Non-login alert",
                 "edit_bottom_text" : "Editing textarea bottom notice",
+                "Check authentication key" : "Check authentication key notice",
+                "email_title" : "Email subject",
+                "email_text" : "Email content",
+                "email_insert_text" : "Email input box text",
+                "password_search_text" : "Password finder text",
+                "reset_user_text" : "Password reset complete text",
             "_comment_2.2.4_" : "Google",
                 "recaptcha" : "reCAPTCHA",
                 "google_imap" : "Google IMAP",
@@ -241,7 +234,7 @@
                 "acl_document_list" : "ACL document(s) list",
                 "acl_required" : "Required ACL",
             "_comment_2.3.2_" : "ACL List",
-                "admin_group_add" : "Admin group add",
+                "admin_group_add" : "Authorize Administrators Group",
                 "ban_authority" : "Block authority",
                 "discussion_authority" : "Discussion manage authority",
                 "user_check_authority" : "User check authority",
@@ -252,6 +245,35 @@
             "_comment_2.3.3_" : "Record",
                 "edit_record" : "Edit record",
                 "discussion_record" : "Discussion record",
+        "_comment_2.4_" : "Editing textarea",
+            "edit_button_link" : "Link",
+            "edit_button_footnote" : "Footnote",
+            "edit_button_macro" : "Macro",
+            "edit_button_color" : "Color",
+            "edit_button_bold" : "Bold",
+            "edit_button_strike" : "Strike",
+            "edit_button_big" : "Big",
+        "_comment_2.5_" : "Topic tool",
+            "topic_tool" : "Discussion tool",
+            "topic_state" : "Discussion status",
+            "topic_stop" : "Stop discussion",
+            "topic_close" : "Close discussion",
+            "topic_restart" : "Discussion restart",
+            "topic_open" : "Open discussion",
+            "topic_agreement" : "Discussion agreement",
+            "topic_destruction" : "Cancel discussion agreement",
+        "_comment_2.6_" : "Period",
+            "1_day" : "1 day",
+            "5_day" : "5 days",
+            "30_day" : "30 days",
+            "180_day" : "180 days",
+            "360_day" : "360 days",
+        "_comment_2.7_" : "ACL",
+            "admin_acl" : "Admin only",
+            "member_acl" : "Member only",
+            "50_edit_acl" : "Only members with 50 or more all document edits",
+            "all_acl" : "All Users",
+            "email_acl" : "Only users with email",
     "_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.",

+ 42 - 20
language/ko-KR.json

@@ -28,7 +28,7 @@
         "backlink" : "역링크",
         "closed" : "닫힘",
         "reload" : "다시 로드",
-        "send" : "보냄",
+        "send" : "전송",
         "ongoing" : "진행중",
         "normal" : "보통",
         "range" : "범위",
@@ -152,10 +152,6 @@
         "user_document" : "사용자 문서",
         "user_head" : "사용자 <head>",
         "user_document_acl" : "사용자 문서 ACL",
-        "admin_acl" : "관리자만",
-        "member_acl" : "가입자만",
-        "50_edit_acl" : "기여 횟수 총합 50회 이상 가입자만",
-        "all_acl" : "모든 사용자",
         "encryption_method" : "암호화 방식",
         "check_key" : "인증키 검사",
         "reset_user_ok" : "검사 성공",
@@ -163,19 +159,10 @@
         "ban_period" : "차단 기간",
         "not_sure" : "확실하지 않음",
         "use_push_alarm" : "푸쉬 알림 사용",
-        "topic_tool" : "토론 도구",
-        "topic_state" : "토론 상태",
-        "topic_stop" : "토론 중지",
-        "topic_close" : "토론 닫기",
-        "topic_restart" : "토론 중지 해제",
-        "topic_open" : "토론 열기",
-        "topic_agreement" : "토론 합의",
-        "topic_destruction" : "토론 합의 취소",
-        "1_day" : "1일",
-        "5_day" : "5일",
-        "30_day" : "30일",
-        "180_day" : "180일",
-        "360_day" : "360일",
+        "edit_button_paragraph" : "문단",
+        "password_change" : "비밀번호 변경",
+        "email_change" : "이메일 변경",
+        "acl_change" : "문서 ACL 변경",
         "_comment_2.1_" : "필터",
             "_comment_2.1.1_" : "목록",
                 "interwiki_list" : "인터위키 목록",
@@ -219,10 +206,16 @@
                 "email_required" : "이메일 필요",
                 "google_imap_required" : "Google IMAP 설정 필요",
                 "update_branch" : "업데이트를 가져올 브랜치",
-            "_comment_2.2.3_" : "문자열",
+            "_comment_2.2.3_" : "문",
                 "register_text" : "회원가입 정책",
                 "non_login_alert" : "비로그인 알림",
                 "edit_bottom_text" : "편집창 하단 문구",
+                "check_key_text" : "인증키 검사 문구",
+                "email_title" : "이메일 제목",
+                "email_text" : "이메일 내용",
+                "email_insert_text" : "이메일 입력란 문구",
+                "password_search_text" : "비밀번호 찾기 문구",
+                "reset_user_text" : "암호 초기화 완료 문구",
             "_comment_2.2.4_" : "Google",
                 "recaptcha" : "reCAPTCHA",
                 "google_imap" : "Google IMAP",
@@ -241,7 +234,7 @@
                 "acl_document_list" : "ACL 문서 목록",
                 "acl_required" : "ACL 필요",
             "_comment_2.3.2_" : "ACL 목록",
-                "admin_group_add" : "관리자 그룹 추가",
+                "admin_group_add" : "관리자 그룹 권한 부여",
                 "ban_authority" : "차단 권한",
                 "discussion_authority" : "토론 관리 권한",
                 "user_check_authority" : "사용자 검사 권한",
@@ -252,6 +245,35 @@
             "_comment_2.3.3_" : "기록",
                 "edit_record" : "편집 기록",
                 "discussion_record" : "토론 기록",
+        "_comment_2.4_" : "편집창",
+            "edit_button_link" : "링크",
+            "edit_button_footnote" : "각주",
+            "edit_button_macro" : "매크로",
+            "edit_button_color" : "색상",
+            "edit_button_bold" : "강조",
+            "edit_button_strike" : "취소선",
+            "edit_button_big" : "크게",
+        "_comment_2.5_" : "토론 도구",
+            "topic_tool" : "토론 도구",
+            "topic_state" : "토론 상태",
+            "topic_stop" : "토론 중지",
+            "topic_close" : "토론 닫기",
+            "topic_restart" : "토론 중지 해제",
+            "topic_open" : "토론 열기",
+            "topic_agreement" : "토론 합의",
+            "topic_destruction" : "토론 합의 취소",
+        "_comment_2.6_" : "기간",
+            "1_day" : "1일",
+            "5_day" : "5일",
+            "30_day" : "30일",
+            "180_day" : "180일",
+            "360_day" : "360일",
+        "_comment_2.7_" : "ACL",
+            "admin_acl" : "관리자만",
+            "member_acl" : "가입자만",
+            "50_edit_acl" : "기여 횟수 총합 50회 이상 가입자만",
+            "all_acl" : "모든 사용자",
+            "email_acl" : "이메일을 가진 유저만",
     "_comment_3_" : "장문",
         "ie_no_data_required" : "이 기능을 수행하는데 필요한 데이터가 제공되지 않았습니다.",
         "oauth_settings_not_found" : "관리자가 이 기능을 수행하는데 필요한 데이터를 제공하지 않았습니다.",

+ 4 - 3
readme-ko.md

@@ -43,9 +43,10 @@ openNAMU는 완전한 오픈소스 프로젝트입니다. 새로운 기능을 
 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/) - [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)
+ * Quotes icon - [Dave Gandy](http://www.flaticon.com/free-icon/quote-left_25672)
+ * Syntax highlighting - [highlightjs](https://highlightjs.org/) 
+ * Numerical expression - [MathJax](https://www.mathjax.org/)
+ * Handling Keyboard Shortcuts [shortcut.js](http://www.openjs.com/scripts/events/keyboard_shortcuts/)
 
 ## 기여자 목록
  * [참고](https://github.com/2DU/openNAMU/graphs/contributors)

+ 2 - 1
readme.md

@@ -69,9 +69,10 @@ openNAMU is open source project. Add new features and request pull requests.
 openNAMU is protected by [BSD 3-Clause License](./LICNESE). Please refer to the documentation for details.
 
 ## External Projects
- * Quotes icon [Dave Gandy](http://www.flaticon.com/free-icon/quote-left_25672) CC 3.0 BY
+ * Quotes icon [Dave Gandy](http://www.flaticon.com/free-icon/quote-left_25672)
  * Syntax highlighting [highlightjs](https://highlightjs.org/)
  * Numerical expression [MathJax](https://www.mathjax.org/)
+ * Handling Keyboard Shortcuts [shortcut.js](http://www.openjs.com/scripts/events/keyboard_shortcuts/)
 
 # Authors
  * [Reference](https://github.com/2DU/openNAMU/graphs/contributors)

+ 4 - 17
route/give_acl.py

@@ -29,22 +29,8 @@ def give_acl_2(conn, name):
                 check_ok = 'disabled'
 
     if flask.request.method == 'POST':
-        if flask.request.form.get('view', None):
-            if flask.request.form.get('dec', '') != flask.request.form.get('view', ''):
-                dec = flask.request.form.get('view', '')
-                view = flask.request.form.get('view', '')
-
-                if view == 'normal':
-                    dec = flask.request.form.get('dec', '') 
-                elif view == 'user':
-                    if dec == 'admin':
-                        dec = flask.request.form.get('dec', '')
-            else:
-                dec = flask.request.form.get('dec', '')
-                view = flask.request.form.get('view', '')
-        else:
-            dec = flask.request.form.get('dec', '')
-            view = flask.request.form.get('view', '')
+        dec = flask.request.form.get('dec', '')
+        view = flask.request.form.get('view', '')
 
         curs.execute("select title from acl where title = ?", [name])
         if curs.fetchall():
@@ -68,7 +54,7 @@ def give_acl_2(conn, name):
         if re.search('^user:', name):
             acl_list = [['', 'normal'], ['user', 'member'], ['all', 'all']]
         else:
-            acl_list = [['', 'normal'], ['user', 'member'], ['admin', 'admin'], ['50_edit', '50 edit']]
+            acl_list = [['', 'normal'], ['user', 'member'], ['admin', 'admin'], ['50_edit', '50 edit'], ['email', 'email']]
         
         curs.execute("select dec from acl where title = ?", [name])
         acl_data = curs.fetchall()
@@ -115,6 +101,7 @@ def give_acl_2(conn, name):
                     <li>member : ''' + load_lang('member_acl') + '''</li>
                     <li>50 edit : ''' + load_lang('50_edit_acl') + '''</li>
                     <li>all : ''' + load_lang('all_acl') + '''</li>
+                    <li>all : ''' + load_lang('email_acl') + '''</li>
                 </ul>
             '''
                 

+ 81 - 45
route/login_check_key.py

@@ -4,33 +4,85 @@ def login_check_key_2(conn, tool):
     curs = conn.cursor()
 
     if flask.request.method == 'POST':
-        if tool == 'check_key':
+        if tool == 'check_pass_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]])
+                hashed = pw_encode(flask.session['c_key'])
 
-                    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]])
+                curs.execute("update user set pw = ? where id = ?", [hashed, flask.session['c_id']])
+                conn.commit()
 
-                    first = 0
+                d_id = flask.session['c_id']
+                pw = flask.session['c_key']
 
-                ip = ip_check()
-                agent = flask.request.headers.get('User-Agent')
+                flask.session.pop('c_id', None)
+                flask.session.pop('c_key', None)
 
-                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()])
+                curs.execute('select data from other where name = "reset_user_text"')
+                sql_d = curs.fetchall()
+                if sql_d and sql_d[0][0] != '':
+                    b_text = sql_d[0][0] + '<hr class=\"main_hr\">'
+                else:
+                    b_text = ''
 
-                flask.session['state'] = 1
-                flask.session['id'] = flask.session['c_id']
-                flask.session['head'] = ''
-                        
-                conn.commit()
+                return easy_minify(flask.render_template(skin_check(),    
+                    imp = [load_lang('reset_user_ok'), wiki_set(), custom(), other2([0, 0])],
+                    data = b_text + load_lang('id') + ' : ' + d_id + '<br>' + load_lang('password') + ' : ' + pw,
+                    menu = [['user', load_lang('return')]]
+                ))
+            else:
+                return redirect('/pass_find')
+        else:
+            ip = ip_check()
+            
+            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()
                 
+                if tool == 'check_key':
+                    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
+    
+                    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()
+                else:
+                    curs.execute('delete from user_set where name = "email" and id = ?', [ip])
+                    curs.execute('insert into user_set (name, id, data) values ("email", ?, ?)', [ip, flask.session['c_email']])
+                    
+                    first = 0
+                          
                 flask.session.pop('c_id', None)
                 flask.session.pop('c_pw', None)
                 flask.session.pop('c_key', None)
@@ -46,36 +98,20 @@ def login_check_key_2(conn, tool):
                 flask.session.pop('c_key', None)
                 flask.session.pop('c_email', None)
 
-                return redirect('/register')
+                return redirect('/user')
+    else:
+        curs.execute('select data from other where name = "check_key_text"')
+        sql_d = curs.fetchall()
+        if sql_d and sql_d[0][0] != '':
+            b_text = sql_d[0][0] + '<hr class=\"main_hr\">'
         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']
+            b_text = ''
 
-                flask.session.pop('c_id', None)
-                flask.session.pop('c_key', None)
-
-                return easy_minify(flask.render_template(skin_check(),    
-                    imp = [load_lang('reset_user_ok'), wiki_set(), custom(), other2([0, 0])],
-                    data =  '''
-                        ''' + load_lang('id') + '''
-                        <hr class=\"main_hr\">
-                        ''' + 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 = [load_lang('key_check'), wiki_set(), custom(), other2([0, 0])],
+            imp = [load_lang('check_key'), wiki_set(), custom(), other2([0, 0])],
             data =  '''
                 <form method="post">
+                    ''' + b_text + '''
                     <input placeholder="''' + load_lang('key') + '''" name="key" type="text">
                     <hr class=\"main_hr\">
                     <button type="submit">''' + load_lang('save') + '''</button>

+ 83 - 29
route/login_need_email.py

@@ -4,7 +4,40 @@ def login_need_email_2(conn, tool):
     curs = conn.cursor()
 
     if flask.request.method == 'POST':
-        if tool == 'need_email':
+        if tool == 'pass_find':
+            curs.execute("select id from user_set where id = ? and name = 'email' and data = ?", [
+                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', '')
+
+                curs.execute('select data from other where name = "email_title"')
+                sql_d = curs.fetchall()
+                if sql_d and sql_d[0][0] != '':
+                    t_text = html.escape(sql_d[0][0])
+                else:
+                    t_text = wiki_set()[0] + ' key'
+
+                curs.execute('select data from other where name = "email_text"')
+                sql_d = curs.fetchall()
+                if sql_d and sql_d[0][0] != '':
+                    i_text = html.escape(sql_d[0][0]) + '\n\nKey : ' + flask.session['c_key']
+                else:
+                    i_text = 'Key : ' + flask.session['c_key']
+
+                send_email(flask.request.form.get('email', ''), t_text, i_text)
+                
+                return redirect('/check_pass_key')
+            else:
+                return re_error('/error/12')
+        else:
+            if tool == 'email_change':
+                flask.session['c_key'] = ''.join(random.choice("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") for i in range(16))
+                flask.session['c_id'] = ip_check()
+                flask.session['c_pw'] = ''
+            
             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', ''))
@@ -18,37 +51,50 @@ def login_need_email_2(conn, tool):
                             flask.session.pop('c_id', None)
                             flask.session.pop('c_pw', None)
                             flask.session.pop('c_key', None)
-
-                            return redirect('/register')
+                            
+                            # user 대신 오류 화면 보여주게 수정 필요
+                            return redirect('/user')
                         else:
-                            send_email(flask.request.form.get('email', ''), wiki_set()[0] + '\'s 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_set where id = ? and name = 'email' and data = ?", [
-                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', '')
+                            curs.execute('select data from other where name = "email_title"')
+                            sql_d = curs.fetchall()
+                            if sql_d and sql_d[0][0] != '':
+                                t_text = html.escape(sql_d[0][0])
+                            else:
+                                t_text = wiki_set()[0] + ' key'
 
-                send_email(flask.request.form.get('email', ''), wiki_set()[0] + '\'s key', 'Key : ' + flask.session['c_key'])
+                            curs.execute('select data from other where name = "email_text"')
+                            sql_d = curs.fetchall()
+                            if sql_d and sql_d[0][0] != '':
+                                i_text = html.escape(sql_d[0][0]) + '\n\nKey : ' + flask.session['c_key']
+                            else:
+                                i_text = 'Key : ' + flask.session['c_key']
 
-                return redirect('/check_pass_key')
-            else:
-                return re_error('/error/12')
+                            send_email(flask.request.form.get('email', ''), t_text, i_text)
+                            flask.session['c_email'] = flask.request.form.get('email', '')
+                  
+                            if tool == 'email_change':
+                                return redirect('/email_replace')
+                            else:
+                                return redirect('/check_key')
+                    else:
+                        return redirect('/email_filter')
+            
+            return redirect('/user')
     else:
-        if tool == 'need_email':
+        if tool == 'pass_find':
+            curs.execute('select data from other where name = "password_search_text"')
+            sql_d = curs.fetchall()
+            if sql_d and sql_d[0][0] != '':
+                b_text = sql_d[0][0] + '<hr class=\"main_hr\">'
+            else:
+                b_text = ''
+
             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\">
+                imp = [load_lang('password_search'), wiki_set(), custom(), other2([0, 0])],
+                data =  b_text + '''
                         <form method="post">
+                            <input placeholder="''' + load_lang('id') + '''" name="id" type="text">
+                            <hr class=\"main_hr\">
                             <input placeholder="''' + load_lang('email') + '''" name="email" type="text">
                             <hr class=\"main_hr\">
                             <button type="submit">''' + load_lang('save') + '''</button>
@@ -57,12 +103,20 @@ def login_need_email_2(conn, tool):
                 menu = [['user', load_lang('return')]]
             ))
         else:
+            curs.execute('select data from other where name = "email_insert_text"')
+            sql_d = curs.fetchall()
+            if sql_d and sql_d[0][0] != '':
+                b_text = sql_d[0][0] + '<hr class=\"main_hr\">'
+            else:
+                b_text = ''
+
             return easy_minify(flask.render_template(skin_check(),    
-                imp = [load_lang('password_search'), wiki_set(), custom(), other2([0, 0])],
+                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\">
+                        ''' + b_text + '''
                         <form method="post">
-                            <input placeholder="''' + load_lang('id') + '''" name="id" type="text">
-                            <hr class=\"main_hr\">
                             <input placeholder="''' + load_lang('email') + '''" name="email" type="text">
                             <hr class=\"main_hr\">
                             <button type="submit">''' + load_lang('save') + '''</button>

+ 51 - 0
route/login_pw_change.py

@@ -0,0 +1,51 @@
+from .tool.func import *
+
+def login_pw_change_2(conn):
+    curs = conn.cursor()
+    
+    if ban_check() == 1:
+        return re_error('/ban')
+
+    if custom()[2] == 0:
+        return redirect('/login')
+
+    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, ip_check()])
+
+            return redirect('/user')
+    else:
+        return easy_minify(flask.render_template(skin_check(), 
+            imp = [load_lang('password_change'), wiki_set(), custom(), other2([0, 0])],
+            data = '''
+                <form method="post">
+                    <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\">
+                    <button type="submit">''' + load_lang('restart') + '''</button>
+                </form>
+            ''',
+            menu = [['change', load_lang('return')]]
+        ))

+ 9 - 1
route/main_manager.py

@@ -1,6 +1,6 @@
 from .tool.func import *
 
-def main_manager_2(conn, num):
+def main_manager_2(conn, num, r_ver):
     curs = conn.cursor()
     
     title_list = {
@@ -26,6 +26,7 @@ def main_manager_2(conn, num):
             data =  '''
                     <h2>''' + load_lang('admin') + '''</h2>
                     <ul>
+                        <li><a href="/manager/2">''' + load_lang('acl_change') + '''</a></li>
                         <li><a href="/manager/3">''' + load_lang('check_user') + '''</a></li>
                         <li><a href="/ban">''' + load_lang('ban') + '''</a></li>
                         <li><a href="/manager/5">''' + load_lang('authorize') + '''</a></li>
@@ -54,6 +55,13 @@ def main_manager_2(conn, num):
                         <li><a href="/oauth_setting">''' + load_lang('oauth_setting') + '''</a></li>
                         <li><a href="/adsense_setting">''' + load_lang('adsense_setting') + '''</a></li>
                     </ul>
+                    <br>
+                    <h2>''' + load_lang('version') + '''</h2>
+                    <ul>
+                        <li>''' + load_lang('version') + ' : ' + r_ver + '''</li>
+                        <li id="ver_send" style="display: none;">''' + load_lang('lastest') + ''' : </li>
+                    </ul>
+                    <script>load_ver();</script>
                     ''',
             menu = [['other', load_lang('return')]]
         ))

+ 1 - 9
route/main_other.py

@@ -1,6 +1,6 @@
 from .tool.func import *
 
-def main_other_2(conn, r_ver):
+def main_other_2(conn):
     curs = conn.cursor()
     
     return easy_minify(flask.render_template(skin_check(), 
@@ -14,7 +14,6 @@ def main_other_2(conn, r_ver):
             <br>
             <h2>''' + load_lang('list') + '''</h2>
             <ul>
-                <li><a href="/manager/2">''' + load_lang('acl_document_list') + '''</a></li>
                 <li><a href="/admin_list">''' + load_lang('admin_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>
@@ -35,13 +34,6 @@ def main_other_2(conn, r_ver):
             <ul>
                 <li><a href="/manager/1">''' + load_lang('admin_tool') + '''</a></li>
             </ul>
-            <br>
-            <h2>''' + load_lang('version') + '''</h2>
-            <ul>
-                <li>''' + load_lang('version') + ' : ' + r_ver + '''</li>
-                <li id="ver_send" style="display: none;">''' + load_lang('lastest') + ''' : </li>
-            </ul>
-            <script>load_ver();</script>
         ''',
         menu = 0
     ))

+ 23 - 9
route/recent_changes.py

@@ -16,7 +16,8 @@ def recent_changes_2(conn, name, tool):
         ban = ''
         select = ''
 
-        div =   '''
+        div = '''
+            <hr class=\"main_hr\">
             <table id="main_table_set">
                 <tbody>
                     <tr>
@@ -30,7 +31,7 @@ def recent_changes_2(conn, name, tool):
                 sql_num = 0      
 
             if tool == 'history':
-                div +=  '''
+                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>
@@ -84,13 +85,22 @@ def recent_changes_2(conn, name, tool):
                     <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)])
+            if flask.request.args.get('set', 'normal') == 'user':
+                curs.execute('''
+                    select id, title, date, ip, send, leng from history 
+                    where title like 'user:%' 
+                    order by date desc 
+                    limit ?, 50
+                ''', [str(sql_num)])
+            else:
+                div = '<a href="?set=user">(' + load_lang('user_document') + ')</a>' + div
+
+                curs.execute('''
+                    select id, title, date, ip, send, leng from history 
+                    where not title like 'user:%' 
+                    order by date desc 
+                    limit ?, 50
+                ''', [str(sql_num)])
 
         data_list = curs.fetchall()
         for data in data_list:    
@@ -211,6 +221,10 @@ def recent_changes_2(conn, name, tool):
             title = load_lang('recent_change')
                 
             div += next_fix('/recent_changes?num=', num, data_list)
+
+            if flask.request.args.get('set', 'normal') == 'user':
+                sub = ' (' + load_lang('user') + ')'
+                menu = [['recent_changes', load_lang('return')]]
         
         if sub == '':
             sub = 0

+ 11 - 6
route/search_goto.py

@@ -1,11 +1,16 @@
 from .tool.func import *
 
-def search_goto_2(conn):
+def search_goto_2(conn, name):
     curs = conn.cursor()
 
-    curs.execute("select title from data where title = ?", [flask.request.form.get('search', 'test')])
-    data = curs.fetchall()
-    if data:
-        return redirect('/w/' + url_pas(flask.request.form.get('search', 'test')))
+    if flask.request.form.get('search', None):
+        data = flask.request.form.get('search', 'test')
     else:
-        return redirect('/search/' + url_pas(flask.request.form.get('search', 'test')))
+        data = name
+
+    curs.execute("select title from data where title = ?", [data])
+    t_data = curs.fetchall()
+    if t_data:
+        return redirect('/w/' + url_pas(data))
+    else:
+        return redirect('/search/' + url_pas(data))

+ 103 - 69
route/setting.py

@@ -111,77 +111,87 @@ def setting_2(conn, num):
 
             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>
-                            <hr class=\"main_hr\">
-                            <input type="text" name="name" value="''' + html.escape(d_list[0]) + '''">
-                            <hr class=\"main_hr\">
-                            <span>''' + load_lang('wiki_logo') + ''' (HTML)</span>
-                            <hr class=\"main_hr\">
-                            <input type="text" name="logo" value="''' + html.escape(d_list[1]) + '''">
-                            <hr class=\"main_hr\">
-                            <span>''' + load_lang('main_page') + '''</span>
-                            <hr class=\"main_hr\">
-                            <input type="text" name="frontpage" value="''' + html.escape(d_list[2]) + '''">
-                            <hr class=\"main_hr\">
-                            <span>''' + load_lang('bottom_text') + ''' (HTML)</span>
-                            <hr class=\"main_hr\">
-                            <input type="text" name="license" value="''' + html.escape(d_list[3]) + '''">
-                            <hr class=\"main_hr\">
-                            <span>''' + load_lang('max_file_size') + ''' [MB]</span>
-                            <hr class=\"main_hr\">
-                            <input 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>
-                            <hr class=\"main_hr\">
-                            <input type="text" name="back_up" value="''' + html.escape(d_list[9]) + '''">
-                            <hr class=\"main_hr\">
-                            <span>''' + load_lang('wiki_skin') + '''</span>
-                            <hr class=\"main_hr\">
-                            <select name="skin">''' + div2 + '''</select>
-                            <hr class=\"main_hr\">
-                            <span>''' + load_lang('default_acl') + '''</span>
-                            <hr class=\"main_hr\">
-                            <select name="edit">''' + div + '''</select>
-                            <hr class=\"main_hr\">
-                            <span>''' + load_lang('default_discussion_acl') + '''</span>
-                            <hr class=\"main_hr\">
-                            <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>
-                            <hr class=\"main_hr\">
-                            <input type="text" name="host" value="''' + html.escape(d_list[16]) + '''">
-                            <hr class=\"main_hr\">
-                            <span>''' + load_lang('wiki_port') + '''</span>
-                            <hr class=\"main_hr\">
-                            <input type="text" name="port" value="''' + html.escape(d_list[10]) + '''">
-                            <hr class=\"main_hr\">
-                            <span>''' + load_lang('wiki_secret_key') + '''</span>
-                            <hr class=\"main_hr\">
-                            <input type="password" name="key" value="''' + html.escape(d_list[11]) + '''">
-                            <hr class=\"main_hr\">
-                            <span>''' + load_lang('update_branch') + '''</span>
-                            <hr class=\"main_hr\">
-                            <select name="update">''' + div3 + '''</select>
-                            <hr class=\"main_hr\">
-                            <span>''' + load_lang('encryption_method') + '''</span>
-                            <hr class=\"main_hr\">
-                            <select name="encode">''' + div5 + '''</select>
-                            <hr class=\"main_hr\">
-                            <button id="save" type="submit">''' + load_lang('save') + '''</button>
-                        </form>
-                        ''',
+                data = '''
+                    <form method="post">
+                        <span>''' + load_lang('wiki_name') + '''</span>
+                        <hr class=\"main_hr\">
+                        <input type="text" name="name" value="''' + html.escape(d_list[0]) + '''">
+                        <hr class=\"main_hr\">
+                        <span>''' + load_lang('wiki_logo') + ''' (HTML)</span>
+                        <hr class=\"main_hr\">
+                        <input type="text" name="logo" value="''' + html.escape(d_list[1]) + '''">
+                        <hr class=\"main_hr\">
+                        <span>''' + load_lang('main_page') + '''</span>
+                        <hr class=\"main_hr\">
+                        <input type="text" name="frontpage" value="''' + html.escape(d_list[2]) + '''">
+                        <hr class=\"main_hr\">
+                        <span>''' + load_lang('bottom_text') + ''' (HTML)</span>
+                        <hr class=\"main_hr\">
+                        <input type="text" name="license" value="''' + html.escape(d_list[3]) + '''">
+                        <hr class=\"main_hr\">
+                        <span>''' + load_lang('max_file_size') + ''' [MB]</span>
+                        <hr class=\"main_hr\">
+                        <input 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>
+                        <hr class=\"main_hr\">
+                        <input type="text" name="back_up" value="''' + html.escape(d_list[9]) + '''">
+                        <hr class=\"main_hr\">
+                        <span>''' + load_lang('wiki_skin') + '''</span>
+                        <hr class=\"main_hr\">
+                        <select name="skin">''' + div2 + '''</select>
+                        <hr class=\"main_hr\">
+                        <span>''' + load_lang('default_acl') + '''</span>
+                        <hr class=\"main_hr\">
+                        <select name="edit">''' + div + '''</select>
+                        <hr class=\"main_hr\">
+                        <span>''' + load_lang('default_discussion_acl') + '''</span>
+                        <hr class=\"main_hr\">
+                        <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>
+                        <hr class=\"main_hr\">
+                        <input type="text" name="host" value="''' + html.escape(d_list[16]) + '''">
+                        <hr class=\"main_hr\">
+                        <span>''' + load_lang('wiki_port') + '''</span>
+                        <hr class=\"main_hr\">
+                        <input type="text" name="port" value="''' + html.escape(d_list[10]) + '''">
+                        <hr class=\"main_hr\">
+                        <span>''' + load_lang('wiki_secret_key') + '''</span>
+                        <hr class=\"main_hr\">
+                        <input type="password" name="key" value="''' + html.escape(d_list[11]) + '''">
+                        <hr class=\"main_hr\">
+                        <span>''' + load_lang('update_branch') + '''</span>
+                        <hr class=\"main_hr\">
+                        <select name="update">''' + div3 + '''</select>
+                        <hr class=\"main_hr\">
+                        <span>''' + load_lang('encryption_method') + '''</span>
+                        <hr class=\"main_hr\">
+                        <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:
-        i_list = ['contract', 'no_login_warring', 'edit_bottom_text']
+        i_list = [
+            'contract', 
+            'no_login_warring', 
+            'edit_bottom_text',
+            'check_key_text',
+            'email_title',
+            'email_text',
+            'email_insert_text',
+            'password_search_text',
+            'reset_user_text'
+        ]
         if flask.request.method == 'POST':
             for i in i_list:
                 curs.execute("update other set data = ? where name = ?", [flask.request.form.get(i, ''), i])
@@ -192,7 +202,7 @@ def setting_2(conn, num):
 
             return redirect('/setting/2')
         else:
-            n_list = ['', '', '']
+            n_list = ['', '', '', '', '', '', '', '', '']
             d_list = []
             
             x = 0
@@ -227,6 +237,30 @@ def setting_2(conn, num):
                             <hr class=\"main_hr\">
                             <input type="text" name="edit_bottom_text" value="''' + html.escape(d_list[2]) + '''">
                             <hr class=\"main_hr\">
+                            <span>''' + load_lang('check_key_text') + ''' (HTML)</span>
+                            <hr class=\"main_hr\">
+                            <input type="text" name="check_key_text" value="''' + html.escape(d_list[3]) + '''">
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('email_title') + '''</span>
+                            <hr class=\"main_hr\">
+                            <input type="text" name="email_title" value="''' + html.escape(d_list[4]) + '''">
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('email_text') + '''</span>
+                            <hr class=\"main_hr\">
+                            <input type="text" name="email_text" value="''' + html.escape(d_list[5]) + '''">
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('email_insert_text') + '''</span>
+                            <hr class=\"main_hr\">
+                            <input type="text" name="email_insert_text" value="''' + html.escape(d_list[6]) + '''">
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('password_search_text') + '''</span>
+                            <hr class=\"main_hr\">
+                            <input type="text" name="password_search_text" value="''' + html.escape(d_list[7]) + '''">
+                            <hr class=\"main_hr\">
+                            <span>''' + load_lang('reset_user_text') + '''</span>
+                            <hr class=\"main_hr\">
+                            <input type="text" name="reset_user_text" value="''' + html.escape(d_list[8]) + '''">
+                            <hr class=\"main_hr\">
                             <button id="save" type="submit">''' + load_lang('save') + '''</button>
                         </form>
                         ''',

+ 26 - 11
route/tool/func.py

@@ -43,12 +43,14 @@ for i in range(0, 2):
             if platform.system() == 'Linux':
                 ok = os.system('python3 -m pip install --user -r requirements.txt')
                 if ok == 0:
+                    print('----')
                     os.execl(sys.executable, sys.executable, *sys.argv)
                 else:
                     raise
             elif platform.system() == 'Windows':
                 ok = os.system('python -m pip install --user -r requirements.txt')
                 if ok == 0:
+                    print('----')
                     os.execl(sys.executable, sys.executable, *sys.argv)
                 else:
                     raise
@@ -194,7 +196,7 @@ def update():
     except:
         pass
 
-    # Start : Data Migration Code
+    # 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'):
@@ -362,11 +364,14 @@ def ip_or_user(data):
 
 def edit_button():
     insert_list = [
-        ['[[|]]', '[[|]]'], 
-        ['[*()]', '[*()]'], 
-        ['{{{#!}}}', '{{{#!}}}'], 
-        ['||<>||', '||<>||'], 
-        ["\\'\\'\\'", "\'\'\'"]
+        ['[[name|view]]', load_lang('edit_button_link')], 
+        ['[* data]', load_lang('edit_button_footnote')], 
+        ['[macro(data)]', load_lang('edit_button_macro')],
+        ['{{{#color data}}}', load_lang('edit_button_color')], 
+        ["\\'\\'\\'data\\'\\'\\'", load_lang('edit_button_bold')],
+        ["~~data~~", load_lang('edit_button_strike')],
+        ["{{{+number data}}}", load_lang('edit_button_big')],
+        ["== name ==", load_lang('edit_button_paragraph')]
     ]
 
     data = ''
@@ -597,7 +602,7 @@ def ip_pas(raw_ip):
         curs.execute("select data from other where name = 'ip_view'")
         data = curs.fetchall()
         if data and data[0][0] != '':
-            ip = '<span style="font-size: 75%;">' + hashlib.md5(bytes(raw_ip, 'utf-8')).hexdigest() + '</span>'
+            ip = re.sub('((?:(?!\.).)+)$', 'xxx', raw_ip)
 
             if not admin_check(1):
                 hide = 1
@@ -775,14 +780,14 @@ def acl_check(name, tool = ''):
                 if ip_or_user(ip) == 1:
                     return 1
 
-                if admin_check(5, 'topic send (' + name + ')') != 1:
+                if admin_check(5) != 1:
                     return 1
 
             if acl_data[0][0] == '50_edit':
                 if ip_or_user(ip) == 1:
                     return 1
                 
-                if admin_check(5, 'topic send (' + name + ')') != 1:
+                if admin_check(5) != 1:
                     curs.execute("select count(title) from history where ip = ?", [ip])
                     count = curs.fetchall()
                     if count:
@@ -793,6 +798,16 @@ def acl_check(name, tool = ''):
                     if count < 50:
                         return 1
 
+            if acl_data[0][0] == 'email':
+                if ip_or_user(ip) == 1:
+                    return 1
+                
+                if admin_check(5) != 1:
+                    curs.execute("select data from user_set where id = ? and name = 'email'", [ip])
+                    email = curs.fetchall()
+                    if not email:
+                        return 1
+
         curs.execute('select data from other where name = "edit"')
         set_data = curs.fetchall()
         if set_data:
@@ -852,7 +867,7 @@ def ban_check(ip = None, tool = None):
     band_d = curs.fetchall()
     if band_d:
         if tool and tool == 'login':
-            if data[0][0] != 'O':
+            if band_d[0][0] != 'O':
                 return 1
         else:
             return 1
@@ -861,7 +876,7 @@ def ban_check(ip = None, tool = None):
     ban_d = curs.fetchall()
     if ban_d:
         if tool and tool == 'login':
-            if data[0][0] != 'O':
+            if ban_d[0][0] != 'O':
                 return 1
         else:
             return 1

+ 4 - 2
route/tool/set_mark/namu.py

@@ -114,6 +114,8 @@ def table_parser(data, cel_data, start_data, num = 0):
     return [all_table, row_style, cel_style, row, cel, table_class, num]
     
 def table_start(data):
+    data = re.sub('\|\|\n(?P<in>(?:(?:(?:(?!\|\|).)+)\n?)+)\n\|\|', '|| \n\g<in> ||', data)
+
     while 1:
         table = re.search('\n((?:(?:(?:(?:\|\|)+(?:(?:(?!\|\|).(?:\n)*)*))+)\|\|(?:\n)?)+)', data)
         if table:
@@ -180,7 +182,7 @@ def middle_parser(data, fol_num, syntax_num, folding_num):
                     
                     data = re.sub('(?:{{{((?:(?! |{{{|}}}|&lt;).)*)(?P<in> ?)|(}}}))', '&#123;&#123;&#123;' + middle_data[0] + '\g<in>', data, 1)
                 else:
-                    if re.search('^(#|@|\+|\-)', middle_data[0]) and not re.search('^(#|@|\+|\-){2}', middle_data[0]):
+                    if re.search('^(#|@|\+|\-)', middle_data[0]) and not re.search('^(#|@|\+|\-){2}|(#|@|\+|\-)\\\\', middle_data[0]):
                         middle_search = re.search('^(#(?:[0-9a-f-A-F]{3}){1,2})', middle_data[0])
                         if middle_search:                            
                             middle_list += ['span']
@@ -452,7 +454,7 @@ def namu(conn, data, title, main_num):
     data = re.sub('&amp;', '&', data)
 
     data = re.sub('\n( +)\|\|', '\n||', data)
-    data = re.sub('\|\|( +)\n', '||\n', data)
+    data = re.sub('\|\|( +)\n', '|| \n', data)
 
     data = re.sub('\n##(((?!\n).)+)', '', data)
            

+ 1 - 1
route/topic_admin.py

@@ -71,7 +71,7 @@ def topic_admin_2(conn, name, sub, num):
     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])],
+        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')]]
     ))

+ 25 - 49
route/login_change_password.py → route/user_setting.py

@@ -1,6 +1,6 @@
 from .tool.func import *
 
-def login_change_password_2(conn, server_init):
+def user_setting_2(conn, server_init):
     curs = conn.cursor()
 
     support_language = server_init.server_set_var['language']['list']
@@ -16,28 +16,6 @@ def login_change_password_2(conn, server_init):
     
     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:
@@ -57,7 +35,7 @@ def login_change_password_2(conn, server_init):
             if data:
                 email = data[0][0]
             else:
-                email = ''
+                email = '-'
 
             div2 = load_skin()
             div3 = ''
@@ -92,31 +70,29 @@ def login_change_password_2(conn, server_init):
 
             return easy_minify(flask.render_template(skin_check(),    
                 imp = [load_lang('user_setting'), wiki_set(), custom(), other2([0, 0])],
-                data =  '''
-                        <form method="post">
-                            <span>''' + load_lang('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>
-                            ''' + http_warring + '''
-                        </form>
-                        ''',
+                data = '''
+                    <form method="post">
+                        <span>''' + load_lang('id') + ''' : ''' + ip + '''</span>
+                        <hr class=\"main_hr\">
+                        <a href="/pw_change">(''' + load_lang('password_change') + ''')</a>
+                        <hr class=\"main_hr\">
+                        <span>''' + load_lang('email') + ''' : ''' + email + '''</span> <a href="/email_change">(''' + load_lang('email_change') + ''')</a>
+                        <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>
+                        ''' + http_warring + '''
+                    </form>
+                ''',
                 menu = [['user', load_lang('return')]]
             ))
     else:

+ 6 - 3
route/view_raw.py

@@ -22,10 +22,13 @@ def view_raw_2(conn, name, sub_title, 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])
+        if admin_check(6) != 1:
+            curs.execute("select data from topic where id = ? and title = ? and sub = ? and block = ''", [str(num), name, sub_title])
+        else:
+            curs.execute("select data from topic where id = ? and title = ? and sub = ?", [str(num), name, sub_title])
         
         v_name = load_lang('discussion_raw')
-        sub = ' (' + str(num) + ')'
+        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:
@@ -44,4 +47,4 @@ def view_raw_2(conn, name, sub_title, num):
             menu = menu
         ))
     else:
-        return redirect('/w/' + url_pas(name))
+        return re_error('/error/3')

+ 6 - 5
route/view_read.py

@@ -127,11 +127,12 @@ def view_read_2(conn, name):
 
         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
+            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')]]

+ 2 - 2
version.json

@@ -1,6 +1,6 @@
 {
     "master" : {
-        "r_ver" : "v3.1.0-stable-05",
+        "r_ver" : "v3.1.1-stable-01",
         "c_ver" : "400001",
         "s_ver" : "2"
     }, "stable" : {
@@ -8,4 +8,4 @@
         "c_ver" : "400001",
         "s_ver" : "2"
     }
-}
+}

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

@@ -35,4 +35,5 @@ body { word-break: break-all; overflow: auto; }
 hr.main_hr { border: none; }
 #include_link { display: none; }
 .foot_plus { background: gainsboro; }
-#toc_title { font-size: 18px; }
+#toc_title { font-size: 18px; }
+blockquote { background-image: url(/views/acme/img/quote.png); background-position: calc(100% - 10px) 10px; background-repeat: no-repeat; background-size: 25px; }

+ 223 - 0
views/main_css/js/shotcuts.js

@@ -0,0 +1,223 @@
+/**
+ * http://www.openjs.com/scripts/events/keyboard_shortcuts/
+ * Version : 2.01.B
+ * By Binny V A
+ * License : BSD
+ */
+shortcut = {
+    'all_shortcuts': {},//All the shortcuts are stored in this array
+    'add': function (shortcut_combination, callback, opt) {
+        //Provide a set of default options
+        var default_options = {
+            'type': 'keydown',
+            'propagate': false,
+            'disable_in_input': false,
+            'target': document,
+            'keycode': false
+        }
+        if (!opt) opt = default_options;
+        else {
+            for (var dfo in default_options) {
+                if (typeof opt[dfo] == 'undefined') opt[dfo] = default_options[dfo];
+            }
+        }
+
+        var ele = opt.target;
+        if (typeof opt.target == 'string') ele = document.getElementById(opt.target);
+        var ths = this;
+        shortcut_combination = shortcut_combination.toLowerCase();
+
+        //The function to be called at keypress
+        var func = function (e) {
+            e = e || window.event;
+
+            if (opt['disable_in_input']) { //Don't enable shortcut keys in Input, Textarea fields
+                var element;
+                if (e.target) element = e.target;
+                else if (e.srcElement) element = e.srcElement;
+                if (element.nodeType == 3) element = element.parentNode;
+
+                if (element.tagName == 'INPUT' || element.tagName == 'TEXTAREA') return;
+            }
+
+            //Find Which key is pressed
+            if (e.keyCode) code = e.keyCode;
+            else if (e.which) code = e.which;
+            var character = String.fromCharCode(code).toLowerCase();
+
+            if (code == 188) character = ","; //If the user presses , when the type is onkeydown
+            if (code == 190) character = "."; //If the user presses , when the type is onkeydown
+
+            var keys = shortcut_combination.split("+");
+            //Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked
+            var kp = 0;
+
+            //Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken
+            var shift_nums = {
+                "`": "~",
+                "1": "!",
+                "2": "@",
+                "3": "#",
+                "4": "$",
+                "5": "%",
+                "6": "^",
+                "7": "&",
+                "8": "*",
+                "9": "(",
+                "0": ")",
+                "-": "_",
+                "=": "+",
+                ";": ":",
+                "'": "\"",
+                ",": "<",
+                ".": ">",
+                "/": "?",
+                "\\": "|"
+            }
+            //Special Keys - and their codes
+            var special_keys = {
+                'esc': 27,
+                'escape': 27,
+                'tab': 9,
+                'space': 32,
+                'return': 13,
+                'enter': 13,
+                'backspace': 8,
+
+                'scrolllock': 145,
+                'scroll_lock': 145,
+                'scroll': 145,
+                'capslock': 20,
+                'caps_lock': 20,
+                'caps': 20,
+                'numlock': 144,
+                'num_lock': 144,
+                'num': 144,
+
+                'pause': 19,
+                'break': 19,
+
+                'insert': 45,
+                'home': 36,
+                'delete': 46,
+                'end': 35,
+
+                'pageup': 33,
+                'page_up': 33,
+                'pu': 33,
+
+                'pagedown': 34,
+                'page_down': 34,
+                'pd': 34,
+
+                'left': 37,
+                'up': 38,
+                'right': 39,
+                'down': 40,
+
+                'f1': 112,
+                'f2': 113,
+                'f3': 114,
+                'f4': 115,
+                'f5': 116,
+                'f6': 117,
+                'f7': 118,
+                'f8': 119,
+                'f9': 120,
+                'f10': 121,
+                'f11': 122,
+                'f12': 123
+            }
+
+            var modifiers = {
+                shift: { wanted: false, pressed: false },
+                ctrl: { wanted: false, pressed: false },
+                alt: { wanted: false, pressed: false },
+                meta: { wanted: false, pressed: false }	//Meta is Mac specific
+            };
+
+            if (e.ctrlKey) modifiers.ctrl.pressed = true;
+            if (e.shiftKey) modifiers.shift.pressed = true;
+            if (e.altKey) modifiers.alt.pressed = true;
+            if (e.metaKey) modifiers.meta.pressed = true;
+
+            for (var i = 0; k = keys[i], i < keys.length; i++) {
+                //Modifiers
+                if (k == 'ctrl' || k == 'control') {
+                    kp++;
+                    modifiers.ctrl.wanted = true;
+
+                } else if (k == 'shift') {
+                    kp++;
+                    modifiers.shift.wanted = true;
+
+                } else if (k == 'alt') {
+                    kp++;
+                    modifiers.alt.wanted = true;
+                } else if (k == 'meta') {
+                    kp++;
+                    modifiers.meta.wanted = true;
+                } else if (k.length > 1) { //If it is a special key
+                    if (special_keys[k] == code) kp++;
+
+                } else if (opt['keycode']) {
+                    if (opt['keycode'] == code) kp++;
+
+                } else { //The special keys did not match
+                    if (character == k) kp++;
+                    else {
+                        if (shift_nums[character] && e.shiftKey) { //Stupid Shift key bug created by using lowercase
+                            character = shift_nums[character];
+                            if (character == k) kp++;
+                        }
+                    }
+                }
+            }
+
+            if (kp == keys.length &&
+						modifiers.ctrl.pressed == modifiers.ctrl.wanted &&
+						modifiers.shift.pressed == modifiers.shift.wanted &&
+						modifiers.alt.pressed == modifiers.alt.wanted &&
+						modifiers.meta.pressed == modifiers.meta.wanted) {
+                callback(e);
+
+                if (!opt['propagate']) { //Stop the event
+                    //e.cancelBubble is supported by IE - this will kill the bubbling process.
+                    e.cancelBubble = true;
+                    e.returnValue = false;
+
+                    //e.stopPropagation works in Firefox.
+                    if (e.stopPropagation) {
+                        e.stopPropagation();
+                        e.preventDefault();
+                    }
+                    return false;
+                }
+            }
+        }
+        this.all_shortcuts[shortcut_combination] = {
+            'callback': func,
+            'target': ele,
+            'event': opt['type']
+        };
+        //Attach the function with the event
+        if (ele.addEventListener) ele.addEventListener(opt['type'], func, false);
+        else if (ele.attachEvent) ele.attachEvent('on' + opt['type'], func);
+        else ele['on' + opt['type']] = func;
+    },
+
+    //Remove the shortcut - just specify the shortcut and I will remove the binding
+    'remove': function (shortcut_combination) {
+        shortcut_combination = shortcut_combination.toLowerCase();
+        var binding = this.all_shortcuts[shortcut_combination];
+        delete (this.all_shortcuts[shortcut_combination])
+        if (!binding) return;
+        var type = binding['event'];
+        var ele = binding['target'];
+        var callback = binding['callback'];
+
+        if (ele.detachEvent) ele.detachEvent('on' + type, callback);
+        else if (ele.removeEventListener) ele.removeEventListener(type, callback, false);
+        else ele['on' + type] = false;
+    }
+}

+ 31 - 0
views/main_css/js/shotcuts_set.js

@@ -0,0 +1,31 @@
+all_list = [
+    ['F', '/'],
+    ['C', '/recent_changes'],
+    ['D', '/recent_discuss'],
+    ['A', '/random']
+];
+
+all_list.forEach(function(element) {
+    shortcut.add(element[0], function() {
+        window.location.href = element[1];
+    }, {
+        'disable_in_input' : true
+    }); 
+});
+
+all_list_2 = [
+    ['W', '/w'],
+    ['H', '/history'],
+    ['E', '/edit'],
+];
+
+all_list_2.forEach(function(element) {    
+    shortcut.add(element[0], function() {
+        href_d = window.location.href.split("/");
+        if(href_d[4]) { 
+            window.location.href = element[1] + '/' + href_d[4];
+        }
+    }, {
+        'disable_in_input' : true
+    });
+});

+ 10 - 0
views/neo_yousoro/css/main.css

@@ -369,4 +369,14 @@ input {
 
 #nav_bar a {
     color: black;
+}
+
+blockquote {
+    padding: 1em calc(2em + 25px) 1em 1em;
+    margin: 1em 0em 0em;
+    background: #eeeeee;
+    display: block;
+    border: 2px dashed #ccc;
+    border-left: 5px solid skyblue;
+    font-size: 14px;
 }