Procházet zdrojové kódy

Merge pull request #570 from 2du/master

뭔가 많이 했는데
surplus_up (2du) před 7 roky
rodič
revize
d0c0fcb1f5

+ 3 - 1
.gitignore

@@ -13,4 +13,6 @@ views/liberty
 views/yousoro
 views/super_lite
 views/buma
-views/before_namu
+views/before_namu
+views/acme/
+views/sl-open/

+ 2 - 16
license.md → LICENSE

@@ -1,6 +1,6 @@
-## BSD 3-Clause License
+BSD 3-Clause License
 
-Copyright (c) 2017, 2DU
+Copyright (c) 2017-2018, 2DU
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -27,17 +27,3 @@ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-## External File License
- * 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/)
-
-## Contributors
- * [Reference](https://github.com/2DU/openNAMU/graphs/contributors)
-
-## Helpful people
- * [Team Croatia](https://github.com/TeamCroatia)
- * Basix
- * Efrit
- * Other chat rooms

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 303 - 183
app.py


+ 25 - 1
emergency_tool.py

@@ -1,5 +1,7 @@
 import json
 import sqlite3
+import bcrypt
+import hashlib
 import threading
 
 from func import *
@@ -14,10 +16,12 @@ curs = conn.cursor()
 load_conn(conn)
 
 print('1. backlink reset')
-print('2. reCAPTCHA delete')
+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 = '')
 what_i_do = input()
@@ -66,6 +70,26 @@ elif what_i_do == '5':
     skin = input()
 
     curs.execute("update other set data = ? where name = 'skin'", [skin])
+elif what_i_do == '6':
+    print('1. sha256')
+    print('2. bcrypt')
+    print('select : ', end = '')
+    what_i_do = input()
+
+    print('user name : ', end = '')
+    user_name = input()
+
+    print('user password : ', end = '')
+    user_pw = input()
+
+    if what_i_do == '1':
+        hashed = hashlib.sha256(bytes(user_pw, 'utf-8')).hexdigest()
+    elif what_i_do == '2':
+        hashed = bcrypt.hashpw(bytes(user_pw, 'utf-8'), bcrypt.gensalt()).decode()
+       
+    curs.execute("update user set pw = ? where id = ?", [hashed, user_name])
+elif what_i_do == '7':
+    curs.execute("update other set data = '00000' where name = 'ver'")
 
 conn.commit()
 

+ 218 - 121
func.py

@@ -1,20 +1,26 @@
 import email.mime.text
-import flask
-import json
+import urllib.request
 import sqlite3
 import hashlib
-import requests
 import smtplib
-import re
+import bcrypt
+import flask
+import json
 import html
+import sys
+import re
 import os
+try:
+    import css_html_js_minify
+except:
+    pass
+
+if sys.version_info < (3, 6):
+    import sha3
 
 from set_mark.tool import *
 from mark import *
 
-json_data = open(os.path.join('language', 'en-US.json'), 'rt', encoding='utf-8').read()
-else_lang = json.loads(json_data)
-
 def load_conn(data):
     global conn
     global curs
@@ -27,30 +33,40 @@ def load_conn(data):
 def send_email(who, title, data):
     smtp = smtplib.SMTP_SSL('smtp.gmail.com', 465)
 
-    curs.execute('select name, data from other where name = "g_email" or name = "g_pass"')
-    rep_data = curs.fetchall()
-    if rep_data:
-        g_email = ''
-        g_pass = ''
-        for i in rep_data:
-            if i[0] == 'g_email':
-                g_email = i[1]
-            else:
-                g_pass = i[1]
+    try:
+        curs.execute('select name, data from other where name = "g_email" or name = "g_pass"')
+        rep_data = curs.fetchall()
+        if rep_data:
+            g_email = ''
+            g_pass = ''
+            for i in rep_data:
+                if i[0] == 'g_email':
+                    g_email = i[1]
+                else:
+                    g_pass = i[1]
 
-        try:
             smtp.login(g_email, g_pass)
-        except:
-            print('error : email login error')
 
-    msg = email.mime.text.MIMEText(data)
-    msg['Subject'] = title
-    smtp.sendmail(g_email, who, msg.as_string())
+        msg = email.mime.text.MIMEText(data)
+        msg['Subject'] = title
+        smtp.sendmail(g_email, who, msg.as_string())
 
-    smtp.quit()
+        smtp.quit()
+    except:
+        print('error : email login error')
 
-def easy_minify(data):
-    data = re.sub('\n +<', '\n<', data)
+def easy_minify(data, tool = None):
+    try:
+        if not tool:
+            data = css_html_js_minify.html_minify(data)
+        else:
+            if tool == 'css':
+                data = css_html_js_minify.css_minify(data)
+            elif tool == 'js':
+                data = css_html_js_minify.js_minify(data)
+    except:
+        data = re.sub('\n +<', '\n<', data)
+        data = re.sub('>(\n| )+<', '> <', data)
     
     return data
 
@@ -113,13 +129,81 @@ def update():
             print('사용자 to user, 파일 to file, 분류 to category')
     except:
         pass
-
-    # v3.0.6 사용자 설정 분리
+        
+    # v3.0.8 rd, agreedis, stop 테이블 통합
+    try:
+        curs.execute("select title, sub, close from stop")
+        for i in curs.fetchall():
+            if i[2] == '':
+                curs.execute("update rd set stop = 'S' where title = ? and sub = ?", [i[0], i[1]])
+            else:
+                curs.execute("update rd set stop = 'O' where title = ? and sub = ?", [i[0], i[1]])
+    except:
+        pass
+        
     try:
-        curs.execute("alter table user drop email")
-        curs.execute("alter table user drop skin")
+        curs.execute("select title, sub from agreedis")
+        for i in curs.fetchall():
+            curs.execute("update rd set agree = 'O' where title = ? and sub = ?", [i[0], i[1]])
     except:
-       pass
+        pass
+         
+    try:
+        curs.execute("drop table if exists stop")
+        curs.execute("drop table if exists agreedis")
+    except:
+        pass
+
+def pw_encode(data, data2 = '', type_d = ''):
+    if type_d == '':
+        curs.execute('select data from other where name = "encode"')
+        set_data = curs.fetchall()
+
+        type_d = set_data[0][0]
+
+    if type_d == 'sha256':
+        return hashlib.sha256(bytes(data, 'utf-8')).hexdigest()
+    elif type_d == 'sha3':
+        if sys.version_info < (3, 6):
+            return sha3.sha3_256(bytes(data, 'utf-8')).hexdigest()
+        else:
+            return hashlib.sha3_256(bytes(data, 'utf-8')).hexdigest()
+    else:
+        if data2 != '':
+            salt_data = bytes(data2, 'utf-8')
+        else:
+            salt_data = bcrypt.gensalt(11)
+            
+        return bcrypt.hashpw(bytes(data, 'utf-8'), salt_data).decode()
+
+def pw_check(data, data2, type_d = 'no', id_d = ''):
+    curs.execute('select data from other where name = "encode"')
+    db_data = curs.fetchall()
+
+    if type_d != 'no':
+        if type_d == '':
+            set_data = 'bcrypt'
+        else:
+            set_data = type_d
+    else:
+        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
+        else:
+            re_data = 0
+
+    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])
+
+    return re_data
 
 def captcha_post(re_data, num = 1):
     if num == 1:
@@ -127,12 +211,13 @@ def captcha_post(re_data, num = 1):
             curs.execute('select data from other where name = "sec_re"')
             sec_re = curs.fetchall()
             if sec_re and sec_re[0][0] != '':
-                data = requests.get('https://www.google.com/recaptcha/api/siteverify', params = { 'secret' : sec_re, 'response' : re_data })
+                data = urllib.request.urlopen('https://www.google.com/recaptcha/api/siteverify?secret=' + sec_re[0][0] + '&response=' + re_data)
                 if not data:
                     return 0
                 else:
-                    json_data = data.json()
-                    if data.status_code == 200 and json_data['success'] == True:
+                    json_data = data.read().decode(data.headers.get_content_charset())
+                    json_data = json.loads(json_data)
+                    if data.getcode() == 200 and json_data['success'] == True:
                         return 0
                     else:
                         return 1
@@ -143,35 +228,18 @@ def captcha_post(re_data, num = 1):
     else:
         pass
 
-def load_lang(data, num = 0):
-    global lang
-
+def load_lang(data, num = 2):
     if num == 1:
-        try:
-            if lang:
-                pass
-        except:
-            curs.execute("select data from other where name = 'language'")
-            rep_data = curs.fetchall()
+        curs.execute("select data from other where name = 'language'")
+        rep_data = curs.fetchall()
 
-            json_data = open(os.path.join('language', rep_data[0][0] + '.json'), 'rt', encoding='utf-8').read()
-            lang = json.loads(json_data)
+        json_data = open(os.path.join('language', rep_data[0][0] + '.json'), 'rt', encoding='utf-8').read()
+        lang = json.loads(json_data)
 
         if data in lang:
             return lang[data]
         else:
-            if data in else_lang:
-                return else_lang[data]
-            else:
-                return data + ' (missing)'
-    elif num == 2:
-        curs.execute('select data from user_set where name = "lang" and id = ?', [ip_check()])
-        rep_data = curs.fetchall()
-        if not rep_data:
-            curs.execute("select data from other where name = 'language'")
-            rep_data = curs.fetchall()
-
-        return rep_data[0][0]
+            return data + ' (missing)'
     else:
         curs.execute('select data from user_set where name = "lang" and id = ?', [ip_check()])
         rep_data = curs.fetchall()
@@ -217,11 +285,11 @@ def edit_help_button():
                 </script>
                 '''
 
-    insert_list = [['[[|]]', 'link'], ['[()]', 'macro'], ['{{{#!}}}', 'middle'], ['||<>||', 'table']]
+    insert_list = [['[[|]]', '[[|]]'], ['[*()]', '[*()]'], ['{{{#!}}}', '{{{#!}}}'], ['||<>||', '||<>||'], ["\\'\\'\\'", "\'\'\'"]]
 
     data = ''
     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>']
 
@@ -340,6 +408,9 @@ def diff(seqm):
     end = end.replace('\r\n', '\n')
     sub = ''
 
+    if not re.search('\n', end):
+        end += '\n'
+
     num = 0
     left = 1
     while 1:
@@ -368,7 +439,7 @@ def diff(seqm):
             
     return sub
            
-def admin_check(num, what):
+def admin_check(num = None, what = None):
     ip = ip_check() 
 
     curs.execute("select acl from user where id = ?", [ip])
@@ -404,6 +475,8 @@ def admin_check(num, what):
                     reset = 1
                 else:
                     break
+                    
+    return 0
 
 def ip_pas(raw_ip):
     hide = 0
@@ -430,7 +503,7 @@ def ip_pas(raw_ip):
             ip = '<a id="not_thing" href="/w/' + url_pas('user:' + raw_ip) + '">' + raw_ip + '</a>'
          
     if hide == 0:
-        ip += ' <a href="/record/' + url_pas(raw_ip) + '">(' + load_lang('record') + ')</a>'
+        ip += ' <a href="/tool/' + url_pas(raw_ip) + '">(' + load_lang('tool') + ')</a>'
 
     return ip
 
@@ -468,6 +541,7 @@ def custom():
 
 def load_skin(data = ''):
     div2 = ''
+    system_file = ['main_css', 'easter_egg.html']
 
     if data == '':
         ip = ip_check()
@@ -475,7 +549,7 @@ def load_skin(data = ''):
         curs.execute('select data from user_set where name = "skin" and id = ?', [ip])
         data = curs.fetchall()
         for skin_data in os.listdir(os.path.abspath('views')):
-            if not skin_data == 'main_css':
+            if not skin_data in system_file:
                 if not data:
                     curs.execute('select data from other where name = "skin"')
                     sql_data = curs.fetchall()
@@ -489,7 +563,7 @@ def load_skin(data = ''):
                     div2 += '<option value="' + skin_data + '">' + skin_data + '</option>'
     else:
         for skin_data in os.listdir(os.path.abspath('views')):
-            if not skin_data == 'main_css':
+            if not skin_data in system_file:
                 if data == skin_data:
                     div2 = '<option value="' + skin_data + '">' + skin_data + '</option>' + div2
                 else:
@@ -499,82 +573,87 @@ def load_skin(data = ''):
 
 def acl_check(name, tool = ''):
     ip = ip_check()
+    
+    if tool == 'render':
+        curs.execute("select view from acl where title = ?", [name])
+        acl_data = curs.fetchall()
+        if acl_data:
+            if acl_data[0][0] == 'user':
+                if not user_data:
+                    return 1
 
-    if ban_check() == 1:
-        return 1
+            if acl_data[0][0] == 'admin':
+                if not user_data:
+                    return 1
 
-    acl_c = re.search("^user:([^/]*)", name)
-    if acl_c:
-        acl_n = acl_c.groups()
+                if not admin_check(5, 'view (' + name + ')') == 1:
+                    return 1
 
-        if admin_check(5, None) == 1:
-            return 0
+        return 0
+    else:
+        if ban_check() == 1:
+            return 1
 
-        curs.execute("select dec from acl where title = ?", ['user:' + acl_n[0]])
-        acl_data = curs.fetchall()
-        if acl_data:
-            if acl_data[0][0] == 'all':
-                return 0
+        acl_c = re.search("^user:([^/]*)", name)
+        if acl_c:
+            acl_n = acl_c.groups()
 
-            if acl_data[0][0] == 'user' and not re.search("(\.|:)", ip):
+            if admin_check(5, None) == 1:
                 return 0
 
-            if ip != acl_n[0] or re.search("(\.|:)", ip):
-                return 1
-        
-        if ip == acl_n[0] and not re.search("(\.|:)", ip) and not re.search("(\.|:)", acl_n[0]):
-            return 0
-        else:
-            return 1
-
-    file_c = re.search("^file:(.*)", name)
-    if file_c and admin_check(5, 'edit (' + name + ')') != 1:
-        return 1
+            curs.execute("select dec from acl where title = ?", ['user:' + acl_n[0]])
+            acl_data = curs.fetchall()
+            if acl_data:
+                if acl_data[0][0] == 'all':
+                    return 0
 
-    curs.execute("select acl from user where id = ?", [ip])
-    user_data = curs.fetchall()
+                if acl_data[0][0] == 'user' and not re.search("(\.|:)", ip):
+                    return 0
 
-    curs.execute("select dec, view from acl where title = ?", [name])
-    acl_data = curs.fetchall()
-    if acl_data:
-        if acl_data[0][0] == 'user':
-            if not user_data:
+                if ip != acl_n[0] or re.search("(\.|:)", ip):
+                    return 1
+            
+            if ip == acl_n[0] and not re.search("(\.|:)", ip) and not re.search("(\.|:)", acl_n[0]):
+                return 0
+            else:
                 return 1
 
-        if acl_data[0][0] == 'admin':
-            if not user_data:
-                return 1
+        file_c = re.search("^file:(.*)", name)
+        if file_c and admin_check(5, 'edit (' + name + ')') != 1:
+            return 1
 
-            if not admin_check(5, 'edit (' + name + ')') == 1:
-                return 1
+        curs.execute("select acl from user where id = ?", [ip])
+        user_data = curs.fetchall()
 
-        if tool == 'render':
-            if acl_data[0][1] == 'user':
+        curs.execute("select dec from acl where title = ?", [name])
+        acl_data = curs.fetchall()
+        if acl_data:
+            if acl_data[0][0] == 'user':
                 if not user_data:
                     return 1
 
-            if acl_data[0][1] == 'admin':
+            if acl_data[0][0] == 'admin':
                 if not user_data:
                     return 1
 
-                if not admin_check(5, 'view (' + name + ')') == 1:
+                if not admin_check(5, 'edit (' + name + ')') == 1:
                     return 1
 
-    curs.execute('select data from other where name = "edit"')
-    set_data = curs.fetchall()
-    if set_data:
-        if set_data[0][0] == 'user':
-            if not user_data:
-                return 1
+        curs.execute('select data from other where name = "edit"')
+        set_data = curs.fetchall()
+        if set_data:
+            if set_data[0][0] == 'login':
+                if not user_data:
+                    return 1
 
-        if set_data[0][0] == 'admin':
-            if not user_data:
-                return 1
+            if set_data[0][0] == 'admin':
+                if not user_data:
+                    return 1
 
-            if not admin_check(5, None) == 1:
-                return 1
+                if not admin_check(5, None) == 1:
+                    return 1
 
-    return 0
+        return 0
 
 def ban_check(ip = None, tool = None):
     if not ip:
@@ -639,7 +718,7 @@ def topic_check(name, sub):
             if not admin_check(3, 'topic (' + name + ')') == 1:
                 return 1
         
-    curs.execute("select title from stop where title = ? and sub = ?", [name, sub])
+    curs.execute("select title from rd where title = ? and sub = ? and not stop = ''", [name, sub])
     if curs.fetchall():
         if not admin_check(3, 'topic (' + name + ')') == 1:
             return 1
@@ -686,10 +765,8 @@ def rd_plus(title, sub, date):
 def history_plus(title, data, date, ip, send, leng):
     curs.execute("select id from history where title = ? order by id + 0 desc limit 1", [title])
     id_data = curs.fetchall()
-    if id_data:
-        curs.execute("insert into history (id, title, data, date, ip, send, leng) values (?, ?, ?, ?, ?, ?, ?)", [str(int(id_data[0][0]) + 1), title, data, date, ip, send, leng])
-    else:
-        curs.execute("insert into history (id, title, data, date, ip, send, leng) values ('1', ?, ?, ?, ?, ?, ?)", [title, data, date, ip, send + ' (' + load_lang('new', 1) + ' ' + load_lang('document', 1) + ')', leng])
+    
+    curs.execute("insert into history (id, title, data, date, ip, send, leng, hide) values (?, ?, ?, ?, ?, ?, ?, '')", [str(int(id_data[0][0]) + 1) if id_data else '1', title, data, date, ip, send, leng])
 
 def leng_check(first, second):
     if first < second:
@@ -701,10 +778,30 @@ def leng_check(first, second):
         
     return all_plus
 
+def edit_filter_do(data):
+    if admin_check(1, 'edit_filter pass') != 1:
+        curs.execute("select regex, sub from filter")
+        for data_list in curs.fetchall():
+            match = re.compile(data_list[0], re.I)
+            if match.search(data):
+                ban_insert(
+                    ip_check(), 
+                    '0' if data_list[1] == 'X' else data_list[1], 
+                    load_lang('edit', 1) + ' ' + load_lang('filter', 1), 
+                    None, 
+                    load_lang('tool', 1) + ':' + load_lang('edit', 1) + ' ' + load_lang('filter', 1)
+                )
+                
+                return 1
+    
+    return 0
+
 def redirect(data):
     return flask.redirect(data)
 
 def re_error(data):
+    conn.commit()
+    
     if data == '/ban':
         ip = ip_check()
 
@@ -765,7 +862,7 @@ def re_error(data):
             elif num == 7:
                 data = load_lang('long_id_error')
             elif num == 8:
-                data = load_lang('id_char_error')
+                data = load_lang('id_char_error') + ' <a href="/name_filter">(' + load_lang('id') + ' ' + load_lang('filter') + ')</a>'
             elif num == 9:
                 data = load_lang('file_exist_error')
             elif num == 10:

+ 1 - 1
language/en-US.json

@@ -26,6 +26,7 @@
     "move" : "move",
     "hide" : "hide",
     "list" : "list",
+    "id" : "id",
     "out" : "out",
     "revert" : "undo",
     "version" : " ver",
@@ -70,7 +71,6 @@
     "alarm" : "alarm",
     "preview" : "preview",
     "watchlist" : "watching list",
-    "my_info" : "user set",
     "state" : "state",
     "recent" : "recent",
     "discussion" : "debate",

+ 1 - 1
language/ko-KR.json

@@ -12,6 +12,7 @@
     "move": "이동",
     "hide": "숨김",
     "list": "목록",
+    "id" : "아이디",
     "revert": "되돌리기",
     "version": "판",
     "normal_version": "버전",
@@ -28,7 +29,6 @@
     "user": "사용자",
     "alarm": "알림",
     "watchlist": "주시 문서",
-    "my_info": "내 정보",
     "recent": "최근",
     "recent_changes": "최근 변경",
     "discussion": "토론",

+ 8 - 3
mark.py

@@ -39,9 +39,14 @@ def plusing(data):
         if not curs.fetchall():
             curs.execute("insert into back (title, link, type) values (?, ?, ?)", [data_in[1], data_in[0], data_in[2]])
 
-def namumark(title = '', data = '', num = 0):
-    if not data == '':
-        data = namu(conn, data, title, num)
+def namumark(title = '', data = None, num = 0):
+    if data != None:
+        curs.execute('select data from other where name = "markup"')
+        rep_data = curs.fetchall()
+        if rep_data[0][0] == 'namumark':
+            data = namu(conn, data, title, num)
+        else:
+            data = ['', '', []]
 
         if num == 1:
             data_num = len(data[2]) 

+ 50 - 13
readme-ko.md

@@ -1,21 +1,58 @@
-## 개요
-나무마크 기반의 파이썬 위키 엔진 입니다. (파이썬 3.5 이상)
+opennamu
+====
+![Python 3.5 이상 필요](https://img.shields.io/badge/python-%3E%3D%203.5-blue.svg)
 
-## 클론 명령어
-### 일반
+오픈나무는 파이썬 기반의 위키 엔진입니다. 파이썬과 그 의존성 모듈만 설치하면 사용할 수 있으며, 코드를 직접 수정하여 좀 더 주제에 특화된 위키를 만들 수 있습니다.
+
+### 목차
+ * [시작하기](#시작하기)
+ * [클론](#클론)
+ * [기여](#기여)
+ * [라이선스](#라이선스)
+ * [기여자 목록](#기여자-목록)
+ * [기타](#기타)
+
+# 시작하기
+ * 오픈나무 위키에 명시되어 있습니다. [참조](http://namu.ml/w/오픈나무%2F설치법)
+
+# 클론
+아래 명령을 터미널(명령 프롬프트)에 입력하여 본 리포지토리를 클론할 수 있습니다.
+## 일반
  * `git clone -b stable https://github.com/2du/opennamu.git`
 
-### 베타
+## 베타
  * `git clone -b master https://github.com/2du/opennamu.git`
 
-## 설치법
- * [참조](http://namu.ml/w/오픈나무%2F설치법)
- 
-## set.json 설명
+# 기여
+오픈나무에는 검증되지 않은 몇가지 버그가 존재할 수 있습니다. 당신의 오픈나무 사용과 버그 발견은 오픈나무의 발전을 돕습니다.
+[이슈 생성하기](https://github.com/2du/opennamu/issues/new)
+
+오픈나무는 완전한 오픈소스 프로젝트입니다. 새로운 기능을 추가하고 Pull Request를 해보세요. [다음 절차]에 따라 기여할 수 있습니다.
+[Pull Requests 생성하기](https://github.com/2du/opennamu/compare)
+
+# 라이선스
+오픈나무는 [BSD 3-Clause License](./LICENSE)에 의해 보호받고 있습니다. 자세한 내용은 문서를 참고하세요.
+
+## 외부 프로젝트 라이선스
+ * 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/)
+
+# 기여자 목록
+ * [참고](https://github.com/2DU/opennamu/graphs/contributors)
+
+## 도움을 주신 분들
+ * [Team Croatia](https://github.com/TeamCroatia)
+ * Basix
+ * Efrit
+ * Other chat rooms
+
+# 기타
+`set.json`은 몇가지 로컬 설정을 저장하는 설정 파일입니다.
  * db = 데이터베이스 이름
 
-set.json를 삭제하면 다시 새로 만들 수 있습니다.
+`set.json`은 삭제해도 다시 새로 만들 수 있습니다.
+
+[테스트 서버](http://namu.ml/)
 
-## 기타
- * [테스트 서버](http://namu.ml/) ([서브 도메인](http://kwee.ga))
- * 첫 번째 가입자에게 소유자 권한이 부여됩니다.
+첫 번째 가입자에게 소유자 권한이 부여됩니다.

+ 51 - 16
readme.md

@@ -1,24 +1,59 @@
-## Intro
-NamuMark based wiki engine returns to Python. (Python 3.5 or later)
+opennamu
+====
+![Python 3.5 or later Required](https://img.shields.io/badge/python-%3E%3D%203.5-blue.svg)
 
-## Clone command
-### Stable
+opennamu is a Python-based wiki engine. If you install Python and its underlying modules, you will be able to create wikis.
+
+ * [(README for korean)](./readme-ko.md)
+
+### Index
+ * [Getting Started](#getting-started)
+ * [Clone](#clone)
+ * [Contribute](#contribute)
+ * [License](#license)
+ * [Authors](#authors)
+ * [Etc.](#etc.)
+
+# Getting Started
+
+# Clone
+You can clone this repository by entering the following command at the terminal (command prompt):
+## Stable
  * `git clone -b stable https://github.com/2du/opennamu.git`
 
-### Master
+## Beta
  * `git clone -b master https://github.com/2du/opennamu.git`
 
-## Install
- * [Reference](https://namu.ml/w/opennamu%2FInstall)
- 
-## set.json Explanation
- * db = Database Name
+# Contribute
+opennamu may have some untested bugs. Your use of opennamu and bug discovery will help develop opennamu.
+[Create Issues](https://github.com/2du/opennamu/issues/new)
+
+opennamu is open source project. Add new features and request pull requests. 
+[Create Pull Requests](https://github.com/2du/opennamu/compare)
+
+# Lisence
+opennamu is protected by [BSD 3-Clause License](./LICNESE). Please refer to the documentation for details.
+
+## External Projects
+ * Quotes icon [Dave Gandy](http://www.flaticon.com/free-icon/quote-left_25672) CC 3.0 BY
+ * Syntax highlighting [highlightjs](https://highlightjs.org/)
+ * Numerical expression [MathJax](https://www.mathjax.org/)
+
+# Authors
+ * [Reference](https://github.com/2DU/opennamu/graphs/contributors)
+
+## Special Thanks
+ * [Team Croatia](https://github.com/TeamCroatia)
+ * Basix
+ * Efrit
+ * Other chat rooms
+
+# Etc.
+`set.json` is a configuration file that stores some local settings.
+ * db = Database name
 
-You can create a new set.json by deleting it.
+If you delete `set.json`, you can create a new one again.
 
-## Other
- * [Test Server](http://namu.ml) ([Sub Domain](http://kwee.ga))
- * The first registor is granted owner privileges.
+[Test Server](http://namu.ml/)
 
-## 한국어
- * [참조](https://github.com/2DU/opennamu/blob/master/Readme-Ko.md)
+Owner rights are granted to the first registor.

+ 8 - 6
requirements.txt

@@ -1,6 +1,8 @@
-tornado >= 4.5.2
-bcrypt >= 3.1.3
-requests >= 2.13.0
-flask >= 0.12.2
-flask-Reggie >= 0.0.2
-flask-compress >= 1.4.0
+tornado
+bcrypt
+flask
+flask-Reggie
+flask-compress
+pysha3; python_version < "3.6"
+css-html-js-minify==2.2.2; python_version < "3.6"
+css-html-js-minify; python_version >= "3.6"

+ 0 - 13
set_mark/html_only.py

@@ -1,13 +0,0 @@
-from . import tool
-
-import datetime
-import html
-import re
-
-def html_only(conn, data, title, main_num):
-	curs = conn.cursor()
-	
-	backlink = []
-	plus_data = ''
-	
-	return data

+ 0 - 5
set_mark/markdown.py

@@ -1,5 +0,0 @@
-from . import tool
-
-import datetime
-import html
-import re

+ 71 - 120
set_mark/namu.py

@@ -161,15 +161,17 @@ def table_start(data):
             
     return data
 
-def middle_parser(data):
+def middle_parser(data, fol_num, syntax_num, folding_num):
     global end_data
+    global plus_data
 
     middle_stack = 0
     middle_list = []
     middle_number = 0
-    fol_num = 0
+
+    middle_re = re.compile('(?:{{{((?:(?:(?! |{{{|}}}|&lt;).)*) ?)|(}}}))')
     while 1:
-        middle_data = re.search('(?:{{{((?:(?! |{{{|}}}|&lt;).)*) ?|(}}}))', data)
+        middle_data = middle_re.search(data)
         if middle_data:
             middle_data = middle_data.groups()
             if not middle_data[1]:
@@ -183,25 +185,25 @@ def middle_parser(data):
                         if middle_search:                            
                             middle_list += ['span']
                             
-                            data = re.sub('(?:{{{((?:(?! |{{{|}}}|&lt;).)*) ?|(}}}))', '<span style="color: ' + middle_search.groups()[0] + ';">', data, 1)
+                            data = middle_re.sub('<span style="color: ' + middle_search.groups()[0] + ';">', data, 1)
                         else:
                             middle_search = re.search('^(?:#(\w+))', middle_data[0])
                             if middle_search:
                                 middle_list += ['span']
                                 
-                                data = re.sub('(?:{{{((?:(?! |{{{|}}}|&lt;).)*) ?|(}}}))', '<span style="color: ' + middle_search.groups()[0] + ';">', data, 1)
+                                data = middle_re.sub('<span style="color: ' + middle_search.groups()[0] + ';">', data, 1)
                             else:
                                 middle_search = re.search('^(?:@((?:[0-9a-f-A-F]{3}){1,2}))', middle_data[0])
                                 if middle_search:
                                     middle_list += ['span']
                                     
-                                    data = re.sub('(?:{{{((?:(?! |{{{|}}}|&lt;).)*) ?|(}}}))', '<span style="background: #' + middle_search.groups()[0] + ';">', data, 1)
+                                    data = middle_re.sub('<span style="background: #' + middle_search.groups()[0] + ';">', data, 1)
                                 else:
                                     middle_search = re.search('^(?:@(\w+))', middle_data[0])
                                     if middle_search:
                                         middle_list += ['span']
                                         
-                                        data = re.sub('(?:{{{((?:(?! |{{{|}}}|&lt;).)*) ?|(}}}))', '<span style="background: ' + middle_search.groups()[0] + ';">', data, 1)
+                                        data = middle_re.sub('<span style="background: ' + middle_search.groups()[0] + ';">', data, 1)
                                     else:
                                         middle_search = re.search('^(\+|-)([1-5])', middle_data[0])
                                         if middle_search:
@@ -213,7 +215,7 @@ def middle_parser(data):
 
                                             middle_list += ['span']
                                             
-                                            data = re.sub('(?:{{{((?:(?! |{{{|}}}|&lt;).)*) ?|(}}}))', '<span style="font-size: ' + font_size + '%;">', data, 1)
+                                            data = middle_re.sub('<span style="font-size: ' + font_size + '%;">', data, 1)
                                         else:
                                             middle_search = re.search('^#!wiki', middle_data[0])
                                             if middle_search:
@@ -235,60 +237,67 @@ def middle_parser(data):
                                                     else:
                                                         middle_data_2 = ['python']
 
+                                                    if syntax_num == 0:
+                                                        plus_data +=    '''
+                                                                        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css">
+                                                                        <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
+                                                                        '''
+
+                                                        syntax_num = 1
+
                                                     middle_list += ['pre']
                                                     
                                                     data = re.sub('{{{#!syntax ?((?:(?!\n).)*)\n?', '<pre id="syntax"><code class="' + middle_data_2[0] + '">', data, 1)
                                                 else:
-                                                    middle_search = re.search('^#!html', middle_data[0])
+                                                    middle_search = re.search('^#!folding', middle_data[0])
                                                     if middle_search:
-                                                        middle_list += ['span']
+                                                        middle_list += ['2div']
                                                         
-                                                        data = re.sub('(?:{{{((?:(?! |{{{|}}}|&lt;).)*) ?|(}}}))', '<span id="html">', data, 1)
-                                                    else:
-                                                        middle_search = re.search('^#!folding', middle_data[0])
-                                                        if middle_search:
-                                                            middle_list += ['2div']
-                                                            
-                                                            folding_data = re.search('{{{#!folding ?((?:(?!\n).)*)\n?', data)
-                                                            if folding_data:
-                                                                folding_data = folding_data.groups()
-                                                            else:
-                                                                folding_data = ['Test']
-                                                            
-                                                            data = re.sub('{{{#!folding ?((?:(?!\n).)*)\n?', '<div>' + str(folding_data[0]) + ' <div style="display: inline-block;"><a href="javascript:void(0);" onclick="folding(' + str(fol_num) + ');">[do]</a></div_end><div id="folding_' + str(fol_num) + '" style="display: none;"><div id="wiki_div" style="">', data, 1)
-                                                            
-                                                            fol_num += 1
+                                                        folding_data = re.search('{{{#!folding ?((?:(?!\n).)*)\n?', data)
+                                                        if folding_data:
+                                                            folding_data = folding_data.groups()
                                                         else:
-                                                            middle_list += ['span']
+                                                            folding_data = ['Test']
+
+                                                        if folding_num == 0:
+                                                            plus_data += '<script src="/views/main_css/parser.js"></script>'
 
-                                                            data = re.sub('(?:{{{((?:(?! |{{{|}}}|&lt;).)*) ?|(}}}))', '<span>', data, 1)
+                                                            folding_num = 1
+                                                        
+                                                        data = re.sub('{{{#!folding ?((?:(?!\n).)*)\n?', '<div>' + str(folding_data[0]) + ' <div style="display: inline-block;"><a href="javascript:void(0);" onclick="folding(' + str(fol_num) + ');">[do]</a></div_end><div id="folding_' + str(fol_num) + '" style="display: none;"><div id="wiki_div" style="">', data, 1)
+                                                        
+                                                        fol_num += 1
+                                                    else:
+                                                        middle_list += ['span']
+
+                                                        data = middle_re.sub('<span>', data, 1)
                     else:
                         middle_list += ['code']
                         
                         middle_stack += 1
                         
-                        data = re.sub('(?:{{{((?:(?! |{{{|}}}|&lt;).)*)|(}}}))', '<code>' + middle_data[0].replace('\\', '\\\\'), data, 1)
+                        data = middle_re.sub('<code>' + middle_data[0].replace('\\', '\\\\'), data, 1)
                 
                     middle_number += 1
             else:
                 if middle_list == []:
-                    data = re.sub('(?:{{{((?:(?! |{{{|}}}|&lt;).)*) ?|(}}}))', '&#125;&#125;&#125;', data, 1)
+                    data = middle_re.sub('&#125;&#125;&#125;', data, 1)
                 else:
                     if middle_stack > 0:
                         middle_stack -= 1
 
                     if middle_stack > 0:
-                        data = re.sub('(?:{{{((?:(?! |{{{|}}}|&lt;).)*) ?|(}}}))', '&#125;&#125;&#125;', data, 1)
+                        data = middle_re.sub('&#125;&#125;&#125;', data, 1)
                     else:                    
                         if middle_number > 0:
                             middle_number -= 1
                             
                         if middle_list[middle_number] == '2div':
-                            data = re.sub('(?:{{{((?:(?! |{{{|}}}|&lt;).)*) ?|(}}}))', '</div_end></div_end></div_end>', data, 1)
+                            data = middle_re.sub('</div_end></div_end></div_end>', data, 1)
                         elif middle_list[middle_number] == 'pre':
-                            data = re.sub('(?:{{{((?:(?! |{{{|}}}|&lt;).)*) ?|(}}}))', '</code></pre>', data, 1)
+                            data = middle_re.sub('</code></pre>', data, 1)
                         else:
-                            data = re.sub('(?:{{{((?:(?! |{{{|}}}|&lt;).)*) ?|(}}}))', '</' + middle_list[middle_number] + '>', data, 1)
+                            data = middle_re.sub('</' + middle_list[middle_number] + '>', data, 1)
                         
                         del(middle_list[middle_number])
         else:
@@ -322,42 +331,7 @@ def middle_parser(data):
         else:
             break
 
-    while 1:
-        html_data = re.search('<span id="html">((?:(?:(?:(?!<\/span>)).)+\n*)+)<\/span>', data)
-        if html_data:
-            html_data = html_data.groups()
-            html_data_2 = html_data[0]
-
-            can_html = ['b', 'span']
-            dic = {}
-
-            for i in can_html:
-                while 1:
-                    test = re.search('&lt;' + i + '((?:(?!&gt;).)*)&gt;', html_data_2)
-                    if test:
-                        test = test.groups()[0]
-                        test = re.sub('&quot;', '"', test)
-                        
-                        html_data_2 = re.sub('&lt;' + i + '((?:(?!&gt;).)*)&gt;', '<' + i + test + '>', html_data_2, 1)
-                    else:
-                        break
-
-            for i in can_html:
-                span_num = re.findall('<' + i + '(?:(?:(?!>).)*)>', html_data_2)
-                span_num = len(span_num)
-                span_end_num = re.findall('<\/' + i + '>', html_data_2)
-                span_end_num = len(span_end_num)
-                
-                dic[i] = span_num - span_end_num
-
-            for i in can_html:
-                html_data_2 += ('</' + i + '>' * dic[i])
-
-            data = re.sub('<span id="html">((?:(?:(?:(?!<\/span>)).)+\n*)+)<\/span>', '<span id="end_html">' + html_data_2 + '<\/span>', data, 1)
-        else:
-            break
-
-    return data
+    return [data, [fol_num, syntax_num, folding_num]]
     
 def link_fix(main_link):
     if re.search('^:', main_link):
@@ -380,21 +354,21 @@ def link_fix(main_link):
 def namu(conn, data, title, main_num):
     curs = conn.cursor()
 
+    global plus_data
+    global end_data
+
     data = '\n' + data + '\n'
+    plus_data = ''
+
     backlink = []
-    plus_data = '''
-                <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css">
-                <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
-                <script src="/views/main_css/parser.js"></script>
-                '''
-    global end_data
     end_data = []
     
     data = html.escape(data)
 
     data = re.sub('\r\n', '\n', data)
 
-    data = middle_parser(data)
+    t_data = middle_parser(data, 0, 0, 0)
+    data = t_data[0]
 
     include_re = re.compile('\[include\(((?:(?!\)\]).)+)\)\]', re.I)
     while 1:
@@ -431,20 +405,20 @@ def namu(conn, data, title, main_num):
             curs.execute("select data from data where title = ?", [include_data])
             include_data = curs.fetchall()
             if include_data:
-                include_parser = include_data[0][0]
+                include_parser = include_re.sub('', include_data[0][0])
+                include_parser = html.escape(include_parser)
 
                 while 1:
                     include_plus = re.search(', ?((?:(?!=).)+)=((?:(?!,).)+)', include)
                     if include_plus:
                         include_plus = include_plus.groups()
-                        include_parser = re.sub('@' + include_plus[0] + '@', include_plus[1], include_parser)
+                        include_parser = include_parser.replace('@' + include_plus[0] + '@', include_plus[1])
 
                         include = re.sub(', ?((?:(?!=).)+)=((?:(?!,).)+)', '', include, 1)
                     else:
                         break
 
                 include_parser = re.sub('\[\[(?:category|분류):(((?!\]\]|#include).)+)\]\]', '', include_parser)
-                include_parser = html.escape(include_parser)
 
                 data = include_re.sub('<include>\n<a id="include_link" href="/w/' + tool.url_pas(include_link) + '">[' + include_link + ']</a>\n' + include_parser + '\n</include>', data, 1)
             else:
@@ -454,41 +428,11 @@ def namu(conn, data, title, main_num):
 
     data = re.sub('\r\n', '\n', data)
 
-    data = middle_parser(data)
+    t_data = middle_parser(data, t_data[1][0], t_data[1][1], t_data[1][2])
+    data = t_data[0]
 
     data = re.sub('&amp;', '&', data)
 
-    curs.execute('select html from html_filter where kind = ""')
-    html_db = curs.fetchall()
-
-    src_list = ["www.youtube.com", "serviceapi.nmv.naver.com", "tv.kakao.com", "www.google.com", "serviceapi.rmcnmv.naver.com"]
-    html_list = ['div', 'span', 'embed', 'iframe', 'ruby', 'rp', 'rt']
-    
-    html_data = re.findall('&lt;(\/)?((?:(?!&gt;| ).)+)( (?:(?:(?!&gt;).)+)?)?&gt;', data)
-    for in_data in html_data:
-        if in_data[0] == '':
-            if in_data[1] in html_list or (html_db and in_data[1] in html_db[0]):
-                if re.search('&lt;\/' + in_data[1] + '&gt;', data):
-                    src = re.search('src=([^ ]*)', in_data[2])
-                    if src:
-                        v_src = re.search('http(?:s)?:\/\/([^/\'" ]*)', src.groups()[0])
-                        if v_src:
-                            if not v_src.groups()[0] in src_list:
-                                and_data = re.sub('&#x27;', '\'', re.sub('&quot;', '"', re.sub('src=([^ ]*)', '', in_data[2])))
-                            else:
-                                and_data = re.sub('&#x27;', '\'', re.sub('&quot;', '"', in_data[2]))
-                        else:
-                            and_data = re.sub('&#x27;', '\'', re.sub('&quot;', '"', re.sub('src=([^ ]*)', '', in_data[2])))
-                    else:
-                        and_data = re.sub('&#x27;', '\'', re.sub('&quot;', '"', in_data[2]))
-                        
-
-                    data = data.replace('&lt;' + in_data[1] + in_data[2] + '&gt;', '<' + in_data[1] + and_data + '>', 1)
-                    data = re.sub('&lt;\/' + in_data[1] + '&gt;', '</' + in_data[1] + '>', data, 1)
-
-    position = re.compile('position', re.I)
-    data = position.sub('', data)
-
     data = re.sub('\n( +)\|\|', '\n||', data)
     data = re.sub('\|\|( +)\n', '||\n', data)
 
@@ -559,7 +503,7 @@ def namu(conn, data, title, main_num):
     data = re.sub('&#x27;&#x27;&#x27;(?P<in>((?!&#x27;&#x27;&#x27;).)+)&#x27;&#x27;&#x27;', '<b>\g<in></b>', data)
     data = re.sub('&#x27;&#x27;(?P<in>((?!&#x27;&#x27;).)+)&#x27;&#x27;', '<i>\g<in></i>', data)
     data = re.sub('~~(?P<in>(?:(?!~~).)+)~~', '<s>\g<in></s>', data)
-    data = re.sub('--(?P<in>(?:(?!~~).)+)--', '<s>\g<in></s>', data)
+    data = re.sub('--(?P<in>(?:(?!--).)+)--', '<s>\g<in></s>', data)
     data = re.sub('__(?P<in>(?:(?!__).)+)__', '<u>\g<in></u>', data)
     data = re.sub('\^\^(?P<in>(?:(?!\^\^).)+)\^\^', '<sup>\g<in></sup>', data)
     data = re.sub(',,(?P<in>(?:(?!,,).)+),,', '<sub>\g<in></sub>', data)
@@ -641,8 +585,8 @@ def namu(conn, data, title, main_num):
     anchor_re = re.compile("\[anchor\((?P<in>(?:(?!\)\]).)+)\)\]", re.I)
     data = anchor_re.sub('<span id="\g<in>"></span>', data)
 
-    ruby_re = re.compile("\[ruby\((?P<in>(?:(?!,).)+)\, ?(?P<out>(?:(?!\)\]).)+)\)\]", re.I)
-    data = ruby_re.sub('<ruby>\g<in><rp>(</rp><rt>\g<out></rt><rp>)</rp></ruby>', data)
+    ruby_re = re.compile("\[ruby\((?P<in>(?:(?!,).)+)\, ?ruby=(?P<out>(?:(?!\)\]|,).)+)(?:\, ?color=(?P<under>(?:(?!\)\]).)+))?\)\]", re.I)
+    data = ruby_re.sub('<ruby>\g<in><rp>(</rp><rt style="color: \g<under>">\g<out></rt><rp>)</rp></ruby>', data)
 
     now_time = tool.get_time()
 
@@ -925,7 +869,10 @@ def namu(conn, data, title, main_num):
                         else:
                             data = re.sub('\[\[((?:(?!\[\[|\]\]).)+)\]\]', '<a href="' + other_link + '">' + see_link + '</a>', data, 1)
                     else:
-                        data = re.sub('\[\[((?:(?!\[\[|\]\]).)+)\]\]', '<b>' + see_link + '</b>', data, 1)
+                        if re.search('^#', other_link):
+                            data = re.sub('\[\[((?:(?!\[\[|\]\]).)+)\]\]', '<a href="' + other_link + '">' + other_link + '</a>', data, 1)
+                        else:
+                            data = re.sub('\[\[((?:(?!\[\[|\]\]).)+)\]\]', '<b>' + see_link + '</b>', data, 1)
                 else:
                     data = re.sub('\[\[((?:(?!\[\[|\]\]).)+)\]\]', '&#91;&#91;' + link + '&#93;&#93;', data, 1)
         else:
@@ -935,12 +882,16 @@ def namu(conn, data, title, main_num):
     data = br_re.sub('<br>', data)
             
     footnote_number = 0
+    
     footnote_all = []
     footnote_dict = {}
     footnote_re = {}
+    
     footdata_all = '\n<hr><ul id="footnote_data">'
+    
+    re_footnote = re.compile('(?:\[\*((?:(?! |\]).)*)(?: ((?:(?!(?:\[\*|\])).)+))?\]|(\[(?:각주|footnote)\]))')
     while 1:
-        footnote = re.search('(?:\[\*((?:(?! |\]).)*)(?: ((?:(?!(?:\[\*(?:(?:(?!\]).)+)\]|\])).)+))?\]|(\[(?:각주|footnote)\]))', data)
+        footnote = re_footnote.search(data)
         if footnote:
             footnote_data = footnote.groups()
             if footnote_data[2]:
@@ -954,7 +905,7 @@ def namu(conn, data, title, main_num):
 
                     footdata_all += '<li><a href="#rfn-' + str(footdata[0]) + '" id="fn-' + str(footdata[0]) + '">(' + footdata[1] + ')</a> ' + footdata_in + '</li>'
                 
-                data = re.sub('(?:\[\*((?:(?! ).)*) ((?:(?!\]).)+)\]|(\[(?:각주|footnote)\]))', footdata_all + '</ul>', data, 1)
+                data = re_footnote.sub(footdata_all + '</ul>', data, 1)
                 
                 footnote_all = []
                 footdata_all = '\n<hr><ul id="footnote_data">'
@@ -970,9 +921,9 @@ def namu(conn, data, title, main_num):
 
                         footnote_all += [[float(footshort), footshort, 0]]
 
-                        data = re.sub('(?:\[\*((?:(?! |\]).)*)(?: ((?:(?!(?:\[\*(?:(?:(?!\]).)+)\]|\])).)+))?\]|(\[(?:각주|footnote)\]))', '<sup><a href="#fn-' + footshort + '" id="rfn-' + footshort + '">(' + footshort + ')</a></sup>', data, 1)
+                        data = re_footnote.sub('<sup><a href="#fn-' + footshort + '" id="rfn-' + footshort + '">(' + footshort + ')</a></sup>', data, 1)
                     else:
-                        data = re.sub('(?:\[\*((?:(?! |\]).)*)(?: ((?:(?!(?:\[\*(?:(?:(?!\]).)+)\]|\])).)+))?\]|(\[(?:각주|footnote)\]))', '<sup><a href="#">(' + footnote_name + ')</a></sup>', data, 1)
+                        data = re_footnote.sub('<sup><a href="#">(' + footnote_name + ')</a></sup>', data, 1)
                 else:
                     footnote_number += 1
 
@@ -988,7 +939,7 @@ def namu(conn, data, title, main_num):
 
                     footnote_all += [[footnote_number, footnote_name, footnote]]
                     
-                    data = re.sub('(?:\[\*((?:(?! |\]).)*)(?: ((?:(?!(?:\[\*(?:(?:(?!\]).)+)\]|\])).)+))?\]|(\[(?:각주|footnote)\]))', '<sup><a href="#fn-' + str(footnote_number) + '" id="rfn-' + str(footnote_number) + '">(' + footnote_name + ')</a></sup>', data, 1)
+                    data = re_footnote.sub('<sup><a href="#fn-' + str(footnote_number) + '" id="rfn-' + str(footnote_number) + '">(' + footnote_name + ')</a></sup>', data, 1)
         else:
             break
 

+ 12 - 3
set_mark/tool.py

@@ -7,9 +7,18 @@ import hashlib
 def get_time():
     return str(datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S"))
     
-def ip_check():
-    if flask.session and ('state' and 'id') in flask.session and flask.session['state'] == 1:
-        ip = flask.session['id']
+def ip_check(d_type = 0):
+    if d_type == 0:
+        if flask.session and ('state' and 'id') in flask.session and flask.session['state'] == 1:
+            ip = flask.session['id']
+        else:
+            try:
+                ip = flask.request.environ.get('HTTP_X_REAL_IP', flask.request.environ.get('HTTP_X_FORWARDED_FOR', flask.request.remote_addr))
+                
+                if ip == ('::1' or '127.0.0.1'):
+                    ip = flask.request.environ.get('HTTP_X_FORWARDED_FOR', flask.request.remote_addr)
+            except:
+                ip = '-'
     else:
         try:
             ip = flask.request.environ.get('HTTP_X_REAL_IP', flask.request.environ.get('HTTP_X_FORWARDED_FOR', flask.request.remote_addr))

+ 2 - 22
views/easter_egg.html

@@ -1,26 +1,6 @@
-<iframe width="560" height="315" src="https://www.youtube.com/embed/Al-C1dQnE0o" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
+<iframe width="560" height="315" src="https://www.youtube.com/embed/O6BJiije6m4" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
 <br>
 <br>
 <div>
-    우리가 바라보았던 그 물결이 지금도 떠오르는걸. 모래 위에 끄적여 새겼던 말들과 돌아선 너의 뒷모습. <br>
-    돌아오는 파도가 발밑을 지나가면서 무언갈 쓸어가네. 해가 질 무렵 쯤에 저녁 노을만이 파도를 따라 흘러가... <br>
-    <br>                
-    팟하면서 피어나는 불꽃놀이를 바라봐. 아직 끝이 나지 않은 여름이. <br>
-    애매할 뿐인 마음을 풀고선 다시 엮어서 오늘 밤이 계속 되길 바랬어... <br>
-    <br>
-    앞으로 더 얼마나 너와 같은 불꽃놀이를 볼 수 있을까? 웃고 있는 네 얼굴에 나는 말을 잃어버려. <br>
-    상처받게되고, 기뻐하게되고 파도처럼 반복된 감정, 초조, 떠나는 열차 소리. <br>
-    몇번이고 나는 또다시 널 부르게 될 꺼야 파도와 함께 너와 한번 더 더 더 더. <br>
-    두번 다시 슬프지않도록 끝나게끔... <br>
-    <br>
-    팟하면서 들이키면 사라질 듯한 불빛이. 아직 여기 마음에 남아있어. <br>
-    손을 뻗어보면 닿았던 따스한 듯한 미래가. 조용하게 우리를 지켜봤어... <br>
-    <br> 
-    팟 한 불꽃들이.  (팟 한 불꽃들이) 오늘 밤 피었어. (오늘 밤 피었어) 오늘 밤 피어선. (오늘 밤 피어선) <br>
-    조용히 사라져. (조용히 사라져) 놓치지 말아줘. (떠나지 말아줘) 조금만 이대로. (조금만 이대로) <br>
-    조금만 이대로 여기에 있어... <br>
-    <br>
-    우리가 바라보았던 그 물결이 지금도 떠오르는걸. 모래 위에 끄적여 새겼던 말들과 돌아선 너의 뒷모습... <br>
-    팟하면서 피어나는 불꽃놀이를 바라봐. 아직 끝이 나지 않은 여름이. <br>
-    애매할 뿐인 마음을 풀고선 다시 엮어서 오늘 밤이 계속 되길 바랬어...
+    노라조와 영접하십셔.
 </div>

+ 3 - 1
views/main_css/main.css

@@ -29,4 +29,6 @@ s:hover, strike:hover, del:hover { color: gray; background-color: gainsboro; tex
 #main_table_set { width: 100%; text-align: center; }
 #main_table_width { width: 33.3%; }
 #main_table_width_half { width: 50%; }
-#redirect { border: 1px solid; padding: 10px; }
+#main_table_width_quarter { width: 25%; }
+#redirect { border: 1px solid; padding: 10px; }
+body { word-break: break-all; overflow: scroll; }

+ 1 - 5
views/main_css/parser.js

@@ -5,10 +5,6 @@ function folding(num) {
     if(fol.style.display == 'inline-block' || fol.style.display == 'block') { 
         fol.style.display = 'none';
     } else {
-        if(num % 2 == 0) { 
-            fol.style.display = 'block'; 
-        } else { 
-            fol.style.display = 'inline-block'; 
-        } 
+        fol.style.display = 'block'; 
     } 
 }

+ 37 - 0
views/main_css/topic_reload.js

@@ -0,0 +1,37 @@
+function topic_load(name, sub) {
+    function addZero(i) {
+        if(i < 10) {
+            i = "0" + i;
+        }
+        
+        return i;
+    }
+
+    setTimeout(function() {
+        var test = setInterval(function() {
+            var d = new Date();
+            d.setSeconds(d.getSeconds() - 3);
+            
+            var date = d.getFullYear() + '-' + addZero(d.getMonth() + 1) + '-' + addZero(d.getDate());
+            date += ' ' + addZero(d.getHours()) + ':' + addZero(d.getMinutes()) + ':' + addZero(d.getSeconds());
+
+            var url = "/api/topic/" + name + "/sub/" + sub + "?time=" + date;
+            var xhr = new XMLHttpRequest();
+            var doc_data = document.getElementById("plus");
+
+            xhr.open("GET", url);
+            xhr.send(null);
+
+            xhr.onreadystatechange = function() {
+                if(this.readyState == XMLHttpRequest.DONE && xhr.status == 200 && xhr.responseText != "{}\n") {
+                    console.log(xhr.responseText);
+                    console.log(url);
+
+                    doc_data.innerText += '(new topic)\n\n';
+
+                    clearInterval(test);
+                }
+            }
+        }, 3000)
+    }, 4000);
+}

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

@@ -66,6 +66,8 @@ li {
     padding: 10px;
     padding-bottom: 20px;
     background: white;
+    border-left: 2px solid gainsboro;
+    border-right: 2px solid gainsboro;
 }
 
 #bottom {
@@ -90,10 +92,27 @@ li {
     padding: 10px;
 }
 
-@media (min-width: 1024px) and (max-width: 1600px) {
+@media (min-width: 780px) and (max-width: 1350px) {
+    #main {
+        width: 90%;
+    }
+
+    #top_main {
+        width: 90%;
+    }
+
+    #bottom_main {
+        width: 90%;
+    }
+
+    .is_mobile {
+        display: none;
+    }
+}
+
+
+@media (min-width: 1350px) and (max-width: 1590px) {
     #main {
-        border-left: 2px solid gainsboro;
-        border-right: 2px solid gainsboro;
         width: 70%;
     }
 
@@ -110,10 +129,8 @@ li {
     }
 }
 
-@media (min-width: 1600px) {
+@media (min-width: 1590px) {
     #main {
-        border-left: 2px solid white;
-        border-right: 2px solid white;
         width: 55%;
     }
 
@@ -130,9 +147,11 @@ li {
     }
 }
 
-@media (max-width: 1024px) {
+@media (max-width: 780px) {
     #main {
         width: 90%;
+        border-left: none;
+        border-right: none;
     }
 
     #top_main {
@@ -314,4 +333,35 @@ input {
 
 #redirect {
     border: 2px solid gainsboro;
+}
+
+#go_toc {
+    display: inline-block;
+    padding: 10px;
+    border-left: 2px solid white;
+    width: 25px;
+}
+
+#go_top {
+    display: inline-block;
+    padding: 10px;
+    border-right: 2px solid white;
+    width: 25px;
+}
+
+#go_bottom {
+    display: inline-block;
+    padding-left: 5px;
+    padding-right: 5px;
+    width: 25px;
+}
+
+#nav_bar {
+    font-size: 24px;
+    position: fixed;
+    bottom: 0;
+    right: 0;
+    border: 2px solid white;
+    background: gainsboro;
+    text-align: center;
 }

+ 17 - 0
views/neo_yousoro/index.html

@@ -161,5 +161,22 @@
                 {{imp[1][1]|safe}}
             </div>
         </div>
+        <div id="nav_bar">
+            <div id="go_top">
+                <a href="#top">
+                    <i class="fas fa-arrow-up"></i>  
+                </a>                  
+            </div>
+            <div id="go_bottom">
+                <a href="#bottom">
+                    <i class="fas fa-arrow-down"></i>
+                </a>
+            </div>
+            <div id="go_toc">
+                <a href="#toc">
+                    <i class="fas fa-list"></i>
+                </a>
+            </div>                                    
+        </div>
     </body>
 </html>

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

@@ -1,3 +1,37 @@
+// 쿠키 생성
+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) {
     var element = document.getElementById(data);
     if(element.style.display == 'none') {
@@ -5,4 +39,40 @@ function opening(data) {
     } else {
         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");
+    }
 }

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů