فهرست منبع

Merge pull request #1950 from openNAMU/dev

Dev
잉여개발기 (SPDV) 2 سال پیش
والد
کامیت
a7019fd6f3

+ 11 - 2
app.py

@@ -159,6 +159,7 @@ with get_db_connect() as conn:
 
     app.config['JSON_AS_ASCII'] = False
     app.config['JSONIFY_PRETTYPRINT_REGULAR'] = True
+    app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 3600
 
     log = logging.getLogger('waitress')
     log.setLevel(logging.ERROR)
@@ -239,7 +240,7 @@ with get_db_connect() as conn:
 
         curs.execute(db_change('select data from other where name = "back_up"'))
         back_time = curs.fetchall()
-        back_time = int(number_check(back_time[0][0])) if back_time and back_time != '' else 0
+        back_time = int(number_check(back_time[0][0])) if back_time and back_time[0][0] != '' else 0
         if back_time != 0:
             curs.execute(db_change('select data from other where name = "backup_where"'))
             back_up_where = curs.fetchall()
@@ -541,7 +542,15 @@ app.route('/bbs/edit/<int:bbs_num>/<int:post_num>/<comment_num>', methods = ['PO
 app.route('/api/w/<everything:name>/doc_tool/<tool>/doc_rev/<int(signed = True):rev>')(api_w)
 app.route('/api/w/<everything:name>/doc_tool/<tool>', methods = ['POST', 'GET'])(api_w)
 app.route('/api/w/<everything:name>', methods = ['GET', 'POST'])(api_w)
-app.route('/api/raw/<everything:name>')(api_raw)
+
+app.route('/api/render_tool/<tool>/<everything:name>', methods = ['POST'])(api_w_render)
+app.route('/api/render_tool/<tool>', methods = ['POST'])(api_w_render)
+app.route('/api/render/<everything:name>', methods = ['POST'])(api_w_render)
+app.route('/api/render', methods = ['POST'])(api_w_render)
+
+app.route('/api/raw_exist/<everything:name>', defaults = { 'exist_check' : 'on' })(api_w_raw)
+app.route('/api/raw_rev/<int(signed = True):rev>/<everything:name>')(api_w_raw)
+app.route('/api/raw/<everything:name>')(api_w_raw)
 
 app.route('/api/bbs/w/<sub_code>')(api_bbs_w_post)
 app.route('/api/bbs/w/comment/<sub_code>')(api_bbs_w_comment)

+ 4 - 2
emergency_tool.py

@@ -269,14 +269,16 @@ elif what_i_do == '24':
     create_data = get_db_table_list()
     for create_table in create_data:
         create = ['test'] + create_data[create_table]
+
         create_r = ', '.join(['%s' for _ in create])
         create = ', '.join(create)
         
-        print('select ' + create + ' from ' + create_table)
         mysql_curs.execute(db_change('delete from ' + create_table))
+        
         sqlite_curs.execute(db_change('select ' + create + ' from ' + create_table))
         db_data = sqlite_curs.fetchall()
-        mysql_curs.executemany(db_change("insert into " + create_table + " (" + create + ") values (" + create_r + ")"), db_data)
+        if db_data:
+            mysql_curs.executemany("insert into " + create_table + " (" + create + ") values (" + create_r + ")", db_data)
 else:
     raise ValueError(what_i_do)
 

+ 2 - 2
lang/en-US.json

@@ -570,8 +570,8 @@
             "error_skin_set" : "The skin you are using does not support individual settings.",
             "error_skin_set_old" : "Some older skins may only work by using the old version link.",
             "same_id_exist_error" : "There are users using the same username.",
-            "long_id_error" : "Username must be shorter than 32 characters.",
-            "id_char_error" : "Only Korean letters and alphabets and numbers are allowed for Username.",
+            "long_id_error" : "Username must be shorter than 128 characters.",
+            "id_char_error" : "XSS characters(inequality symbol, quotation marks) and . and : are not available.",
             "file_exist_error" : "The file does not exist.",
             "password_error" : "Password does not match.",
             "recaptcha_error" : "Pass the reCAPTCHA.",

+ 1 - 1
lang/ko-KR.json

@@ -231,7 +231,7 @@
     "file_name": "파일명",
     "close_discussion": "닫힌 토론",
     "language": "언어",
-    "id_char_error": "XSS 문자(부등호, 따옴표)는 사용 불가능합니다.",
+    "id_char_error": "XSS 문자(부등호, 따옴표)와 .과 :는 사용 불가능합니다.",
     "id_filter_add": "ID 필터 추가",
     "skin": "스킨",
     "user_head": "사용자 <HEAD>",

+ 2 - 1
route/__init__.py

@@ -1,7 +1,8 @@
 from route.api_func_lang import api_func_lang
 from route.api_func_sha224 import api_func_sha224
 from route.api_image_view import api_image_view
-from route.api_raw import api_raw
+from route.api_w_raw import api_w_raw
+from route.api_w_render import api_w_render
 from route.api_recent_change import api_recent_change
 from route.api_recent_discuss import api_recent_discuss
 from route.api_search import api_search

+ 0 - 4
route/api_preview.py

@@ -1,4 +0,0 @@
-from .tool.func import *
-
-def api_preview():
-    pass

+ 13 - 9
route/api_raw.py → route/api_w_raw.py

@@ -1,26 +1,30 @@
 from .tool.func import *
 
-def api_raw(name = 'Test'):
+def api_w_raw(name = 'Test', rev = '', exist_check = ''):
     with get_db_connect() as conn:
         curs = conn.cursor()
 
-        if acl_check(name, 'render') != 1:
-            rev = flask.request.args.get('num', '')
+        if exist_check != '':
+            curs.execute(db_change("select title from data where title = ?"), [name])
+            if data:
+                return flask.jsonify({ 'exist' : '1' })
+            else:
+                return flask.jsonify({})
+        else:
             if rev != '':
                 curs.execute(db_change("select data from history where title = ? and id = ?"), [name, rev])
             else:
                 curs.execute(db_change("select data from data where title = ?"), [name])
+
             data = curs.fetchall()
             if data:
-                json_data = {
+                return flask.jsonify({
                     "title" : name, 
                     "data" : render_set(
                         doc_name = name, 
                         doc_data = data[0][0],
                         data_type = 'raw'
                     )
-                }
-
-                return flask.jsonify(json_data)
-
-        return flask.jsonify({})
+                })
+            else:
+                return flask.jsonify({})

+ 6 - 0
route/api_w_render.py

@@ -0,0 +1,6 @@
+from .tool.func import *
+
+def api_w_render(name = '', tool = ''):
+    data_org = flask.request.form.get('data', '')
+    
+    pass

+ 83 - 143
route/edit_move.py

@@ -41,13 +41,12 @@ def edit_move(name):
             if do_edit_text_bottom_check_box_check(agree) == 1:
                 return re_error('/error/29')
 
+            # 역링크 관련 패치 해야할 듯
+
             # 문서 이동 파트 S
             curs.execute(db_change("select title from history where title = ?"), [move_title])
             if curs.fetchall():
-                if (
-                    move_option == 'merge' and 
-                    admin_check(None, 'merge documents (' + name + ') (' + move_title + ')') == 1
-                ):
+                if move_option == 'merge' and admin_check(None, 'merge documents (' + name + ') (' + move_title + ')') == 1:
                     curs.execute(db_change("select data from data where title = ?"), [move_title])
                     data = curs.fetchall()
                     if data:
@@ -56,151 +55,117 @@ def edit_move(name):
 
                     curs.execute(db_change("select data from data where title = ?"), [name])
                     data = curs.fetchall()
-                    if data:
-                        curs.execute(db_change("update data set title = ? where title = ?"), [move_title, name])
-                        curs.execute(db_change("update back set link = ? where link = ?"), [move_title, name])
+                    data_in = data[0][0] if data else ''
 
-                        data_in = data[0][0]
-                    else:
-                        data_in = ''
-
-                    history_plus(
-                        name,
-                        data_in,
-                        time,
-                        ip,
-                        send,
-                        '0',
-                        t_check = 'merge <a>' + name + '</a> - <a>' + move_title + '</a> move',
-                        mode = 'move'
-                    )
+                    curs.execute(db_change("update data set title = ? where title = ?"), [move_title, name])
+                    curs.execute(db_change("update back set link = ? where link = ?"), [move_title, name])
 
-                    curs.execute(db_change("update back set type = 'no' where title = ? and not type = 'cat' and not type = 'no'"), [name])
-                    curs.execute(db_change("delete from back where title = ? and not type = 'cat' and type = 'no'"), [move_title])
+                    # 역링크 S
+                    # 문서 합치기이므로 기존 문서 쪽은 no 역링크 생성, 이동하는 곳에는 no 역링크 제거
+                    curs.execute(db_change("select distinct link from back where title = ?"), [name])
+                    backlink = [[for_a[0], name, 'no', ''] for for_a in curs.fetchall()]
+                    curs.executemany(db_change("insert into back (link, title, type, data) values (?, ?, ?, ?)"), backlink)
+                    curs.execute(db_change("delete from back where title = ? and type = 'no'"), [move_title])
+                    # 역링크 E
 
                     curs.execute(db_change('select data from other where name = "count_all_title"'))
                     curs.execute(db_change("update other set data = ? where name = 'count_all_title'"), [str(int(curs.fetchall()[0][0]) - 1)])
 
                     curs.execute(db_change("select id from history where title = ? order by id + 0 desc limit 1"), [move_title])
-                    data = curs.fetchall()
-
-                    num = data[0][0]
+                    num = curs.fetchall()[0][0]
 
                     curs.execute(db_change("select id from history where title = ? order by id + 0 asc"), [name])
                     data = curs.fetchall()
                     for move in data:
-                        curs.execute(db_change("update rc set title = ?, id = ? where title = ? and id = ?"), [
-                            move_title, 
-                            str(int(num) + int(move[0])), 
-                            name, 
-                            move[0]
-                        ])
-                        curs.execute(db_change("update history set title = ?, id = ? where title = ? and id = ?"), [
-                            move_title, 
-                            str(int(num) + int(move[0])), 
-                            name, 
-                            move[0]
-                        ])
+                        curs.execute(db_change("update rc set title = ?, id = ? where title = ? and id = ?"), [move_title, str(int(num) + int(move[0])), name, move[0]])
+                        curs.execute(db_change("update history set title = ?, id = ? where title = ? and id = ?"), [move_title, str(int(num) + int(move[0])), name, move[0]])
+
+                    history_plus(move_title, data_in, time, ip, send, '0',
+                        t_check = 'merge <a>' + name + '</a> - <a>' + move_title + '</a> move',
+                        mode = 'move'
+                    )
                 elif move_option == 'reverse':
+                    # 전체적인 구조 변경 필요
+                    # 중간 문서 거치지 않고 불러와서 바로 변경하도록
+                    # 문서 이동 말고 나머지도 그렇게 변경 필요함
                     i = 0
                     var_name = ''
                     while var_name == '':
-                        curs.execute(db_change("select title from history where title = ?"), ['test ' + str(i)])
+                        temp_title = 'test ' + load_random_key() + ' ' + str(i)
+                        curs.execute(db_change("select title from history where title = ? limit 1"), [temp_title])
                         if not curs.fetchall():
-                            var_name = 'test ' + str(i)
+                            var_name = temp_title
                         else:
                             i += 1
 
-                    curs.execute(db_change("select data from data where title = ?"), [name])
-                    data = curs.fetchall()
-                    if data:
-                        curs.execute(db_change("update data set title = ? where title = ?"), [var_name, name])
-                        curs.execute(db_change("update back set link = ? where link = ?"), [var_name, name])
+                    for title_name in [[name, var_name], [move_title, name], [var_name, move_title]]:
+                        curs.execute(db_change("update data set title = ? where title = ?"), [title_name[1], title_name[0]])
+                        curs.execute(db_change("update back set link = ? where link = ?"), [title_name[1], title_name[0]])
 
-                    curs.execute(db_change("update history set title = ? where title = ?"), [var_name, name])
-                    curs.execute(db_change("update rc set title = ? where title = ?"), [var_name, name])
+                        curs.execute(db_change("update history set title = ? where title = ?"), [title_name[1], title_name[0]])
+                        curs.execute(db_change("update rc set title = ? where title = ?"), [title_name[1], title_name[0]])
 
-                    for title_name in [[move_title, name], [var_name, move_title]]:
-                        curs.execute(db_change("select data from data where title = ?"), [title_name[0]])
+                    for title_name in [[name, move_title], [move_title, name]]:
+                        curs.execute(db_change("select data from data where title = ?"), [name])
                         data = curs.fetchall()
-                        if data:
-                            curs.execute(db_change("update data set title = ? where title = ?"), [title_name[1], title_name[0]])
-                            curs.execute(db_change("update back set link = ? where link = ?"), [title_name[1], title_name[0]])
+                        data_in = data[0][0] if data else ''
 
-                            data_in = data[0][0]
-                        else:
-                            data_in = ''
-
-                        history_plus(
-                            title_name[0],
-                            data_in,
-                            time,
-                            ip,
-                            send,
-                            '0',
-                            t_check = '<a>' + (title_name[0] if title_name[0] != var_name else name) + '</a> - <a>' + title_name[1] + '</a> move',
+                        history_plus(title_name[0], data_in, time, ip, send, '0',
+                            t_check = '<a>' + title_name[0] + '</a> - <a>' + title_name[1] + '</a> move',
                             mode = 'move'
                         )
-
-                        curs.execute(db_change("update history set title = ? where title = ?"), [title_name[1], title_name[0]])
-                        curs.execute(db_change("update rc set title = ? where title = ?"), [title_name[1], title_name[0]])
                 elif move_option != 'none':
                     has_error = 1
-            elif move_option != 'none':                
+            elif move_option != 'none':
                 curs.execute(db_change("select data from data where title = ?"), [name])
                 data = curs.fetchall()
-                if data:
-                    curs.execute(db_change("update data set title = ? where title = ?"), [move_title, name])
-                    curs.execute(db_change("update back set link = ? where link = ?"), [move_title, name])
+                data_in = data[0][0] if data else ''
 
-                    data_in = data[0][0]
-                else:
-                    data_in = ''
-
-                history_plus(
-                    name,
-                    data_in,
-                    time,
-                    ip,
-                    send,
-                    '0',
-                    t_check = '<a>' + name + '</a> - <a>' + move_title + '</a> move',
-                    mode = 'move'
-                )
+                curs.execute(db_change("update data set title = ? where title = ?"), [move_title, name])
+                curs.execute(db_change("update back set link = ? where link = ?"), [move_title, name])
 
-                curs.execute(db_change("update back set type = 'no' where title = ? and not type = 'cat' and not type = 'no'"), [name])
-                curs.execute(db_change("delete from back where title = ? and not type = 'cat' and type = 'no'"), [move_title])
+                # 역링크 S
+                # 문서 합치기 쪽 역링크와 동일하게
+                curs.execute(db_change("select distinct link from back where title = ?"), [name])
+                backlink = [[for_a[0], name, 'no', ''] for for_a in curs.fetchall()]
+                curs.executemany(db_change("insert into back (link, title, type, data) values (?, ?, ?, ?)"), backlink)
+                curs.execute(db_change("delete from back where title = ? and type = 'no'"), [move_title])
+                # 역링크 E
 
+                # 역사와 최근 변경 이동 S
                 curs.execute(db_change("update history set title = ? where title = ?"), [move_title, name])
                 curs.execute(db_change("update rc set title = ? where title = ?"), [move_title, name])
-                
+                # 역사와 최근 변경 이동 E
+
+                history_plus(move_title, data_in, time, ip, send, '0',
+                    t_check = '<a>' + name + '</a> - <a>' + move_title + '</a> move',
+                    mode = 'move'
+                )
+
             # 문서 이동 파트 E
             
             # 토론 이동 파트 S
-            if (
-                move_option_topic == 'merge' and
-                admin_check(None, 'merge document\'s topics (' + name + ') (' + move_title + ')') == 1
-            ):
-                curs.execute(db_change("update rd set title = ? where title = ?"), [move_title, name])
-            elif move_option_topic == 'reverse':
-                i = 0
-                var_name = ''
-                while var_name == '':
-                    curs.execute(db_change("select title from rd where title = ?"), ['test ' + str(i)])
-                    if not curs.fetchall():
-                        var_name = 'test ' + str(i)
-                    else:
-                        i += 1
-                
-                curs.execute(db_change("update rd set title = ? where title = ?"), [var_name, move_title])
-                curs.execute(db_change("update rd set title = ? where title = ?"), [move_title, name])
-                curs.execute(db_change("update rd set title = ? where title = ?"), [name, var_name])
-            elif move_option_topic == 'normal':
-                curs.execute(db_change("select title from rd where title = ?"), [move_title])
-                if curs.fetchall():
-                    has_error = 1
-                else:
+            curs.execute(db_change("select title from rd where title = ?"), [move_title])
+            if curs.fetchall():
+                if move_option_topic == 'merge' and admin_check(None, 'merge document\'s topics (' + name + ') (' + move_title + ')') == 1:
                     curs.execute(db_change("update rd set title = ? where title = ?"), [move_title, name])
+                elif move_option_topic == 'reverse':
+                    i = 0
+                    var_name = ''
+                    while var_name == '':
+                        temp_title = 'test ' + load_random_key() + ' ' + str(i)
+                        curs.execute(db_change("select title from rd where title = ? limit 1"), [temp_title])
+                        if not curs.fetchall():
+                            var_name = temp_title
+                        else:
+                            i += 1
+                    
+                    for title_name in [[name, var_name], [move_title, name], [var_name, move_title]]:
+                        curs.execute(db_change("update rd set title = ? where title = ?"), [title_name[1], title_name[0]])
+                else:
+                    has_error = 1
+            elif move_option_topic != 'none':
+                curs.execute(db_change("update rd set title = ? where title = ?"), [move_title, name])
 
             # 토론 이동 파트 E
 
@@ -209,46 +174,21 @@ def edit_move(name):
                 i = 0
                 var_name = ''
                 while var_name == '':
-                    curs.execute(db_change("select title from rd where title = ?"), ['test ' + str(i)])
+                    temp_title = 'test ' + load_random_key() + ' ' + str(i)
+                    curs.execute(db_change("select title from history where title = ? limit 1"), [temp_title])
                     if not curs.fetchall():
-                        var_name = 'test ' + str(i)
+                        var_name = temp_title
                     else:
                         i += 1
                 
-                # create_data['data_set'] = ['doc_name', 'doc_rev', 'set_name', 'set_data']
-                # create_data['acl'] = ['title', 'data', 'type']
-                curs.execute(db_change("update data_set set doc_name = ? where doc_name = ?"), [var_name, move_title])
-                curs.execute(db_change("update data_set set doc_name = ? where doc_name = ?"), [move_title, name])
-                curs.execute(db_change("update data_set set doc_name = ? where doc_name = ?"), [name, var_name])
-
-                curs.execute(db_change("update acl set title = ? where title = ?"), [var_name, move_title])
-                curs.execute(db_change("update acl set title = ? where title = ?"), [move_title, name])
-                curs.execute(db_change("update acl set title = ? where title = ?"), [name, var_name])
+                for title_name in [[name, var_name], [move_title, name], [var_name, move_title]]:
+                    curs.execute(db_change("update data_set set doc_name = ? where doc_name = ?"), [title_name[1], title_name[0]])
             elif document_set_option == 'normal':
                 curs.execute(db_change("delete from data_set where doc_name = ?"), [move_title])
-                curs.execute(db_change("update data_set set doc_name = ? where doc_name = ?"), [move_title, name])
-
                 curs.execute(db_change("delete from acl where title = ?"), [move_title])
-                curs.execute(db_change("update acl set title = ? where title = ?"), [move_title, name])
-
-            if document_set_option != 'reverse':
-                curs.execute(db_change("select data from data where title = ?"), [name])
-                db_data = curs.fetchall()
-                if db_data:
-                    render_set(
-                        doc_name = name,
-                        doc_data = db_data[0][0],
-                        data_type = 'backlink'
-                    )
 
-                curs.execute(db_change("select data from data where title = ?"), [move_title])
-                db_data = curs.fetchall()
-                if db_data:
-                    render_set(
-                        doc_name = move_title,
-                        doc_data = db_data[0][0],
-                        data_type = 'backlink'
-                    )
+                curs.execute(db_change("update data_set set doc_name = ? where doc_name = ?"), [move_title, name])
+                curs.execute(db_change("update acl set title = ? where title = ?"), [move_title, name])
 
             # data_set 이동 파트 E
                 

+ 3 - 1
route/main_view.py

@@ -8,7 +8,9 @@ def main_view(name = ''):
             return main_func_error_404()
         else:
             file_name = file_name.group(1)
-            dir_name = './views/' + re.sub(r'\.{2,}', '', re.sub(r'([^/]+)$', '', name))
+            dir_name = './views/' + re.sub(r'\.{2,}', '', name[:-len(file_name)])
+
+            file_name = re.sub(r'\.cache_v(?:[0-9]+)$', '', file_name)
 
             mime_type = file_name.split('.')
             if len(mime_type) < 2:

+ 51 - 23
route/tool/func.py

@@ -1030,36 +1030,59 @@ def skin_check(set_n = 0):
             return skin
     
 def wiki_css(data):
+    global global_wiki_set
+
     # without_DB
     data += ['' for _ in range(0, 3 - len(data))]
     
     data_css = ''
+    data_css_add = ''
+
     data_css_ver = '182'
-    
-    # Func JS + Defer
-    data_css += '<script src="/views/main_css/js/func/func.js?ver=' + data_css_ver + '"></script>'
-    
-    data_css += '<script defer src="/views/main_css/js/func/insert_version.js?ver=' + data_css_ver + '"></script>'
-    data_css += '<script defer src="/views/main_css/js/func/insert_user_info.js?ver=' + data_css_ver + '"></script>'
-    data_css += '<script defer src="/views/main_css/js/func/insert_version_skin.js?ver=' + data_css_ver + '"></script>'
-    data_css += '<script defer src="/views/main_css/js/func/insert_http_warning_text.js?ver=' + data_css_ver + '"></script>'
-    
-    data_css += '<script defer src="/views/main_css/js/func/ie_end_of_life.js?ver=' + data_css_ver + '"></script>'
-    data_css += '<script defer src="/views/main_css/js/func/shortcut.js?ver=' + data_css_ver + '"></script>'
-    
-    # Route JS + Defer
+    data_css_ver = '.cache_v' + data_css_ver
 
+    if 'main_css' in global_wiki_set:
+        data_css = global_wiki_set['main_css']
+    else:
+        data_css += '<meta http-equiv="Cache-Control" content="max-age=3600">'
+
+        # Func JS
+        data_css += '<script src="/views/main_css/js/func/func.js' + data_css_ver + '"></script>'
+        
+        data_css += '<script defer src="/views/main_css/js/func/insert_version.js' + data_css_ver + '"></script>'
+        data_css += '<script defer src="/views/main_css/js/func/insert_user_info.js' + data_css_ver + '"></script>'
+        data_css += '<script defer src="/views/main_css/js/func/insert_version_skin.js' + data_css_ver + '"></script>'
+        data_css += '<script defer src="/views/main_css/js/func/insert_http_warning_text.js' + data_css_ver + '"></script>'
+        
+        data_css += '<script defer src="/views/main_css/js/func/ie_end_of_life.js' + data_css_ver + '"></script>'
+        data_css += '<script defer src="/views/main_css/js/func/shortcut.js' + data_css_ver + '"></script>'
+        
+        # Route JS
+        data_css += '<script src="/views/main_css/js/route/editor.js' + data_css_ver + '"></script>'
+        data_css += '<script src="/views/main_css/js/route/editor_sub.js' + data_css_ver + '"></script>'
+        data_css += '<script src="/views/main_css/js/route/render.js' + data_css_ver + '"></script>'
+        data_css += '<script src="/views/main_css/js/route/topic.js' + data_css_ver + '"></script>'
+        
+        # Main CSS
+        data_css += '<link rel="stylesheet" href="/views/main_css/css/main.css' + data_css_ver + '">'
+
+        # External
+        data_css += '<script src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js" integrity="sha384-cpW21h6RZv/phavutF+AuVYrr+dA8xD9zs6FwLpaCct6O9ctzYFfFr4dgmgccOTx" crossorigin="anonymous"></script>'
+        data_css += '<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js" integrity="sha512-rdhY3cbXURo13l/WU9VlaRyaIYeJ/KBakckXIvJNAQde8DgpOmE+eZf7ha4vdqVjTtwQt69bD2wH2LXob/LB7Q==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>'
+        data_css += '<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/x86asm.min.js" integrity="sha512-HeAchnWb+wLjUb2njWKqEXNTDlcd1QcyOVxb+Mc9X0bWY0U5yNHiY5hTRUt/0twG8NEZn60P3jttqBvla/i2gA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>'
+
+        # External CSS
+        data_css += '<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css" integrity="sha384-GvrOXuhMATgEsSwCs4smul74iXGOixntILdUW9XmUC6+HX0sLNAK3q71HotJqlAn" crossorigin="anonymous">'
     
-    # Route JS
-    data_css += '<script src="/views/main_css/js/route/editor.js?ver=' + data_css_ver + '"></script>'
-    data_css += '<script src="/views/main_css/js/route/editor_sub.js?ver=' + data_css_ver + '"></script>'
-    data_css += '<script src="/views/main_css/js/route/render.js?ver=' + data_css_ver + '"></script>'
-    data_css += '<script src="/views/main_css/js/route/topic.js?ver=' + data_css_ver + '"></script>'
-    
-    # Main CSS
-    data_css += '<link rel="stylesheet" href="/views/main_css/css/main.css?ver=' + data_css_ver + '">'
+        global_wiki_set['main_css'] = data_css
+
+    # Darkmode
+    if flask.request.cookies.get('main_css_darkmode', '0') == '0':
+        data_css += '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/default.min.css" integrity="sha512-hasIneQUHlh06VNBe7f6ZcHmeRTLIaQWFd43YriJ0UND19bvYRauxthDg8E4eVNPm9bRUhr5JGeqH7FRFXQu5g==" crossorigin="anonymous" referrerpolicy="no-referrer" />'
+    else:
+        data_css += '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/dark.min.css" integrity="sha512-bfLTSZK4qMP/TWeS1XJAR/VDX0Uhe84nN5YmpKk5x8lMkV0D+LwbuxaJMYTPIV13FzEv4CUOhHoc+xZBDgG9QA==" crossorigin="anonymous" referrerpolicy="no-referrer" />'
 
-    data = data[0:2] + ['', data_css] + data[2:]
+    data = data[0:2] + ['', data_css_add + data_css] + data[2:]
 
     return data
 
@@ -1355,7 +1378,8 @@ def render_set(doc_name = '', doc_data = '', data_type = 'view', data_in = '', d
 
                             .opennamu_render_complete summary {
                                 list-style: none !important;
-                                font-weight: bold;
+                                
+                                font-weight: bold !important;
                             }
                         </style>
                     ''' + get_class_render[0]
@@ -1613,6 +1637,10 @@ def do_user_name_check(user_name):
         if html.escape(user_name) != user_name:
             return 1
 
+        # IP와 혼동 방지 
+        if ip_or_user(user_name) == 1:
+            return 1
+
         # ID 필터
         curs.execute(db_change('select html from html_filter where kind = "name"'))
         set_d = curs.fetchall()

+ 32 - 29
route/tool/func_render_namumark.py

@@ -51,10 +51,8 @@ class class_do_render_namumark:
             self.render_data = html.escape(self.render_data)
 
         self.render_data = '<back_br>\n' + self.render_data + '\n<front_br>'
-        self.render_data_cdn = ''
         self.render_data_js = ''
 
-
         self.curs.execute(db_change('select data from other where name = "link_case_insensitive"'))
         db_data = self.curs.fetchall()
         self.link_case_insensitive = ' collate nocase' if db_data and db_data[0][0] != '' else ''
@@ -780,10 +778,6 @@ class class_do_render_namumark:
         def do_render_math_sub(match):
             data = match.group(1)
 
-            if self.data_math_count == 0:
-                self.render_data_cdn += '<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css" integrity="sha384-GvrOXuhMATgEsSwCs4smul74iXGOixntILdUW9XmUC6+HX0sLNAK3q71HotJqlAn" crossorigin="anonymous">'
-                self.render_data_cdn += '<script src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js" integrity="sha384-cpW21h6RZv/phavutF+AuVYrr+dA8xD9zs6FwLpaCct6O9ctzYFfFr4dgmgccOTx" crossorigin="anonymous"></script>'
-
             data = re.sub(r'\n', '', data)
             data = self.get_tool_data_revert(data)
 
@@ -899,12 +893,12 @@ class class_do_render_namumark:
                         db_data = self.curs.fetchall()
                         if db_data:
                             link_exist = ''
-                            self.data_backlink += [[self.doc_name, 'file:' + link_main, 'file', '']]
                         else:
                             link_exist = 'opennamu_not_exist_link'
                             self.data_backlink += [[self.doc_name, 'file:' + link_main, 'no', '']]
-                            self.data_backlink += [[self.doc_name, 'file:' + link_main, 'file', '']]
-                        
+
+                        self.data_backlink += [[self.doc_name, 'file:' + link_main, 'file', '']]
+
                         link_extension_regex = r'\.([^.]+)$'
                         link_extension = re.search(link_extension_regex, link_main)
                         if link_extension:
@@ -990,7 +984,6 @@ class class_do_render_namumark:
                     category_blur = ''
                     if re.search(r'#blur$', link_main, flags = re.I):
                         link_main = re.sub(r'#blur$', '', link_main, flags = re.I)
-
                         category_blur = 'opennamu_category_blur'
                     
                     link_sub = link_main
@@ -1013,7 +1006,12 @@ class class_do_render_namumark:
                             self.data_backlink += [[self.doc_name, 'category:' + link_main, 'no', '']]
 
                         self.data_backlink += [[self.doc_name, 'category:' + link_main, 'cat', '']]
-                        self.data_backlink += [[self.doc_name, 'category:' + link_main, 'cat_view', link_view]]
+                        
+                        if link_view != '':
+                            self.data_backlink += [[self.doc_name, 'category:' + link_main, 'cat_view', link_view]]
+                        
+                        if category_blur != '':
+                            self.data_backlink += [[self.doc_name, 'category:' + link_main, 'cat_blur', '']]
 
                         link_main = url_pas(link_main)
 
@@ -1138,11 +1136,11 @@ class class_do_render_namumark:
                         db_data = self.curs.fetchall()
                         if not db_data:
                             self.data_backlink += [[self.doc_name, link_main, 'no', '']]
-                            self.data_backlink += [[self.doc_name, link_main, '', '']]
                             link_exist = 'opennamu_not_exist_link'
                         else:
                             link_main = db_data[0][0]
-                            self.data_backlink += [[self.doc_name, link_main, '', '']]
+                        
+                        self.data_backlink += [[self.doc_name, link_main, '', '']]
 
                     link_same = ''
                     if link_main == self.doc_name:
@@ -1274,11 +1272,12 @@ class class_do_render_namumark:
                     include_name = self.get_tool_data_restore(include_name, do_type = 'slash')
                     include_name = html.unescape(include_name)
 
+                    self.data_backlink += [[self.doc_name, include_name, 'include', '']]
+
                     # load include db data
                     self.curs.execute(db_change("select data from data where title = ?"), [include_name])
                     db_data = self.curs.fetchall()
                     if db_data:
-                        self.data_backlink += [[self.doc_name, include_name, 'include', '']]
                         include_data = db_data[0][0].replace('\r', '')
 
                         # include link func
@@ -1469,6 +1468,16 @@ class class_do_render_namumark:
                 link_main = self.get_tool_data_restore(link_main, do_type = 'slash')
                 link_main = html.unescape(link_main)
 
+                link_exist = 1
+
+                self.curs.execute(db_change("select title from data where title = ?" + self.link_case_insensitive), [link_main])
+                db_data = self.curs.fetchall()
+                if not db_data:
+                    self.data_backlink += [[self.doc_name, link_main, 'no', '']]
+                    link_exist = 0
+                else:
+                    link_main = db_data[0][0]
+
                 self.data_backlink += [[self.doc_name, link_main, 'redirect', '']]
 
                 link_main = url_pas(link_main)
@@ -1476,11 +1485,14 @@ class class_do_render_namumark:
                     link_main = '/w_from/' + link_main
 
                 self.data_redirect = 1
-                if 'doc_from' in self.doc_set:
-                    data_name = self.get_tool_data_storage('<a href="' + link_main + link_data_sharp + '">(GO)</a>', '', link_data_full)
+                if link_exist == 1:
+                    if 'doc_from' in self.doc_set:
+                        data_name = self.get_tool_data_storage('<a href="' + link_main + link_data_sharp + '">(GO)</a>', '', link_data_full)
+                    else:
+                        data_name = self.get_tool_data_storage('<meta http-equiv="refresh" content="0; url=' + link_main + link_data_sharp + '">', '', link_data_full)
                 else:
-                    data_name = self.get_tool_data_storage('<meta http-equiv="refresh" content="0; url=' + link_main + link_data_sharp + '">', '', link_data_full)
-                    
+                    data_name = self.get_tool_data_storage('', '', link_data_full)
+
                 self.render_data = '<' + data_name + '></' + data_name + '>'
             else:
                 self.curs.execute(db_change("select plus, plus_t from html_filter where kind = 'inter_wiki' and html = ?"), [link_inter_name])
@@ -1798,9 +1810,9 @@ class class_do_render_namumark:
                         wiki_data_end = self.do_inter_render(wiki_data, self.doc_include + 'opennamu_folding_' + str(folding_count))
 
                         middle_data_pass = wiki_data_folding
-                        data_name = self.get_tool_data_storage('<details><summary>', '</summary>', middle_data_org)
+                        data_name = self.get_tool_data_storage('<details><summary>', '</summary><div class="opennamu_folding">', middle_data_org)
 
-                        data_name_2 = self.get_tool_data_storage('', '</details>', '')
+                        data_name_2 = self.get_tool_data_storage('', '</div></details>', '')
                         middle_data_add = '<' + data_name_2 + '>' + wiki_data_end + '</' + data_name_2 + '>'
 
                         folding_count += 1
@@ -1825,13 +1837,6 @@ class class_do_render_namumark:
 
                         if syntax_count == 0:
                             self.render_data_js += 'hljs.highlightAll();\n'
-                            if self.darkmode == '0':
-                                self.render_data_cdn += '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/default.min.css" integrity="sha512-hasIneQUHlh06VNBe7f6ZcHmeRTLIaQWFd43YriJ0UND19bvYRauxthDg8E4eVNPm9bRUhr5JGeqH7FRFXQu5g==" crossorigin="anonymous" referrerpolicy="no-referrer" />'
-                            else:
-                                self.render_data_cdn += '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/dark.min.css" integrity="sha512-bfLTSZK4qMP/TWeS1XJAR/VDX0Uhe84nN5YmpKk5x8lMkV0D+LwbuxaJMYTPIV13FzEv4CUOhHoc+xZBDgG9QA==" crossorigin="anonymous" referrerpolicy="no-referrer" />'
-
-                            self.render_data_cdn += '<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js" integrity="sha512-rdhY3cbXURo13l/WU9VlaRyaIYeJ/KBakckXIvJNAQde8DgpOmE+eZf7ha4vdqVjTtwQt69bD2wH2LXob/LB7Q==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>'
-                            self.render_data_cdn += '<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/x86asm.min.js" integrity="sha512-HeAchnWb+wLjUb2njWKqEXNTDlcd1QcyOVxb+Mc9X0bWY0U5yNHiY5hTRUt/0twG8NEZn60P3jttqBvla/i2gA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>'
 
                         data_name = self.get_tool_data_storage('<pre id="syntax"><code class="' + wiki_data_syntax + '">' + wiki_data, '</code></pre>', middle_data_org)
                         syntax_count += 1
@@ -2338,8 +2343,6 @@ class class_do_render_namumark:
 
         self.render_data = re.sub(r'<a fn_target="([^"]+)"', do_render_last_footnote, self.render_data)
 
-        self.render_data = self.render_data_cdn + self.render_data
-
     def __call__(self):
         self.do_render_remark()
         self.do_render_include_default()

+ 0 - 0
route/view_category.py


+ 9 - 5
route/view_read.py

@@ -32,7 +32,7 @@ def view_read(name = 'Test', doc_rev = '', doc_from = '', do_type = ''):
             count_sub_category = 0
             count_category = 0
 
-            curs.execute(db_change("select link from back where title = ? and type = 'cat' order by link asc"), [name])
+            curs.execute(db_change("select distinct link from back where title = ? and type = 'cat' order by link asc"), [name])
             category_sql = curs.fetchall()
             for data in category_sql:
                 link_view = data[0]
@@ -41,19 +41,23 @@ def view_read(name = 'Test', doc_rev = '', doc_from = '', do_type = ''):
                     db_data = curs.fetchall()
                     if db_data and db_data[0][0] != '':
                         link_view = db_data[0][0]
+                        
+                link_blur = ''
+                curs.execute(db_change("select data from back where title = ? and link = ? and type = 'cat_blur' limit 1"), [name, data[0]])
+                db_data = curs.fetchall()
+                if db_data:
+                    link_blur = 'opennamu_category_blur'
 
                 if data[0].startswith('category:'):
-                    category_sub += '<li><a href="/w/' + url_pas(data[0]) + '">' + html.escape(link_view) + '</a></li>'
-
+                    category_sub += '<li><a class="' + link_blur + '" href="/w/' + url_pas(data[0]) + '">' + html.escape(link_view) + '</a></li>'
                     count_sub_category += 1
                 else:
                     category_doc += '' + \
                         '<li>' + \
-                            '<a href="/w/' + url_pas(data[0]) + '">' + html.escape(link_view) + '</a> ' + \
+                            '<a class="' + link_blur + '" href="/w/' + url_pas(data[0]) + '">' + html.escape(link_view) + '</a> ' + \
                             '<a class="opennamu_link_inter" href="/xref/' + url_pas(data[0]) + '">(' + load_lang('backlink') + ')</a>' + \
                         '</li>' + \
                     ''
-
                     count_category += 1
 
             if category_sub != '':

+ 1 - 9
route/view_xref.py

@@ -26,15 +26,7 @@ def view_xref(name = 'Test', xref_type = 1):
         link_case_insensitive = ' collate nocase' if db_data and db_data[0][0] != '' else ''
 
         sql_insert = ['link', 'title'] if xref_type == 1 else ['title', 'link']
-        curs.execute(db_change("" + \
-            "select distinct " + sql_insert[0] + ", type from back " + \
-            "where " + sql_insert[1] + " = ?" + link_case_insensitive + " and not type = 'no' and not type = 'nothing' " + \
-            "order by type asc, " + sql_insert[0] + " asc limit ?, 50" + \
-        ""), [
-            name,
-            sql_num
-        ])
-
+        curs.execute(db_change("select distinct " + sql_insert[0] + ", type from back where " + sql_insert[1] + " = ?" + link_case_insensitive + " and not type = 'no' and not type = 'nothing' order by type asc, " + sql_insert[0] + " asc limit ?, 50"), [name, sql_num])
         data_list = curs.fetchall()
         for data in data_list:
             div += '<li><a href="/w/' + url_pas(data[0]) + '">' + html.escape(data[0]) + '</a>'

+ 2 - 2
route_go/bin/main_func_easter_egg.amd64.bin

@@ -1,7 +1,7 @@
 {
     "beta" : {
-        "r_ver" : "v3.4.6-RC5-dev14",
+        "r_ver" : "v3.4.6-RC5-dev19",
         "c_ver" : "3500373",
-        "s_ver" : "3500111"
+        "s_ver" : "3500112"
     }
 }

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

@@ -45,14 +45,14 @@ td {
     }
 }
 
-summary {
+/* 폴딩 */
+details summary {
     cursor: pointer;
     user-select: none;
     
     display: list-item;
-    list-style: inside disclosure-closed;
 }
-    
+
 /* list */
 .opennamu_ul {
     padding-left: 20px;
@@ -382,6 +382,7 @@ s:hover, strike:hover, del:hover {
     list-style: none;
 }
 
+/* 이 부분 이하로는 레거시 */
 #topic_color {
     background: #bbeabb;
 }

+ 120 - 0
views/main_css/js/func/func.js

@@ -1,5 +1,125 @@
 "use strict";
 
+// https://css-tricks.com/how-to-animate-the-details-element/
+class Accordion {
+    constructor(el) {
+        // Store the <details> element
+        this.el = el;
+        // Store the <summary> element
+        this.summary = el.querySelector('summary');
+        // Store the <div class="content"> element
+        this.content = el.querySelector('.opennamu_folding');
+    
+        // Store the animation object (so we can cancel it if needed)
+        this.animation = null;
+        // Store if the element is closing
+        this.isClosing = false;
+        // Store if the element is expanding
+        this.isExpanding = false;
+        // Detect user clicks on the summary element
+        this.summary.addEventListener('click', (e) => this.onClick(e));
+    }
+  
+    onClick(e) {
+        // Stop default behaviour from the browser
+        e.preventDefault();
+        // Add an overflow on the <details> to avoid content overflowing
+        this.el.style.overflow = 'hidden';
+        // Check if the element is being closed or is already closed
+        if(this.isClosing || !this.el.open) {
+            this.open();
+        // Check if the element is being openned or is already open
+        } else if(this.isExpanding || this.el.open) {
+            this.shrink();
+        }
+    }
+  
+    shrink() {
+        // Set the element as "being closed"
+        this.isClosing = true;
+        
+        // Store the current height of the element
+        const startHeight = `${this.el.offsetHeight}px`;
+        // Calculate the height of the summary
+        const endHeight = `${this.summary.offsetHeight}px`;
+        
+        // If there is already an animation running
+        if(this.animation) {
+            // Cancel the current animation
+            this.animation.cancel();
+        }
+        
+        // Start a WAAPI animation
+        this.animation = this.el.animate({
+        // Set the keyframes from the startHeight to endHeight
+            height: [startHeight, endHeight]
+        }, {
+            duration: 200,
+            easing: 'ease-out'
+        });
+        
+        // When the animation is complete, call onAnimationFinish()
+        this.animation.onfinish = () => this.onAnimationFinish(false);
+        // If the animation is cancelled, isClosing variable is set to false
+        this.animation.oncancel = () => this.isClosing = false;
+    }
+  
+    open() {
+        // Apply a fixed height on the element
+        this.el.style.height = `${this.el.offsetHeight}px`;
+        // Force the [open] attribute on the details element
+        this.el.open = true;
+        // Wait for the next frame to call the expand function
+        window.requestAnimationFrame(() => this.expand());
+    }
+  
+    expand() {
+        // Set the element as "being expanding"
+        this.isExpanding = true;
+        // Get the current fixed height of the element
+        const startHeight = `${this.el.offsetHeight}px`;
+        // Calculate the open height of the element (summary height + content height)
+        const endHeight = `${this.summary.offsetHeight + this.content.offsetHeight}px`;
+        
+        // If there is already an animation running
+        if(this.animation) {
+            // Cancel the current animation
+            this.animation.cancel();
+        }
+        
+        // Start a WAAPI animation
+        this.animation = this.el.animate({
+        // Set the keyframes from the startHeight to endHeight
+            height: [startHeight, endHeight]
+        }, {
+            duration: 200,
+            easing: 'ease-out'
+        });
+        // When the animation is complete, call onAnimationFinish()
+        this.animation.onfinish = () => this.onAnimationFinish(true);
+        // If the animation is cancelled, isExpanding variable is set to false
+        this.animation.oncancel = () => this.isExpanding = false;
+    }
+  
+    onAnimationFinish(open) {
+      // Set the open attribute based on the parameter
+      this.el.open = open;
+      // Clear the stored animation
+      this.animation = null;
+      // Reset isClosing & isExpanding
+      this.isClosing = false;
+      this.isExpanding = false;
+      // Remove the overflow hidden and the fixed height
+      this.el.style.height = this.el.style.overflow = '';
+    }
+}
+
+window.addEventListener('DOMContentLoaded', function() {
+    document.querySelectorAll('details').forEach((el) => {
+        new Accordion(el);
+    });
+});
+
 function opennamu_do_id_check(data) {
     if(data.match(/\.|\:/)) {
         return 0;

+ 3 - 0
views/main_css/js/route/render_sub.ts

@@ -0,0 +1,3 @@
+function opennamu_do_footnote() {
+    
+}

+ 10 - 7
views/ringo/index.html

@@ -10,15 +10,15 @@
         {{imp[3][3]|safe}}
         <link href="https://cdn.jsdelivr.net/gh/sunn-us/SUIT/fonts/static/woff2/SUIT.css" rel="stylesheet">
         <script src="https://code.iconify.design/1/1.0.3/iconify.min.js"></script>
-        <script src="/views/ringo/js/main.js?ver=2"></script>
-        <script src="/views/ringo/js/sidebar.js?ver=1"></script>
-        <script src="/views/ringo/js/skin_set.js?ver=1"></script>
-        <link rel="stylesheet" href="/views/ringo/css/main.css?ver=4">
+        <script src="/views/ringo/js/main.js.cache_v2"></script>
+        <script src="/views/ringo/js/sidebar.js.cache_v1"></script>
+        <script src="/views/ringo/js/skin_set.js.cache_v1"></script>
+        <link rel="stylesheet" href="/views/ringo/css/main.css.cache_v4">
         {% if request.cookies.get('main_css_darkmode', '') == '1' %}
-            <link rel="stylesheet" href="/views/main_css/css/sub/dark.css?ver=1">
-            <link rel="stylesheet" href="/views/ringo/css/dark.css?ver=1">
+            <link rel="stylesheet" href="/views/main_css/css/sub/dark.css.cache_v1">
+            <link rel="stylesheet" href="/views/ringo/css/dark.css.cache_v1">
         {% endif %}
-        <link rel="shortcut icon" href="/views/main_css/file/favicon.ico?ver=1">
+        <link rel="shortcut icon" href="/views/main_css/file/favicon.ico.cache_v1">
         {{imp[1][5]|safe}}
         {{imp[2][3]|safe}}
         {% if imp[3][0] != 0 %}
@@ -192,6 +192,9 @@
                             <sub>{{imp[3][0]}}</sub>
                         {% endif %}
                     </h1>
+                    {% if imp[3][1] != 0 %}
+                        {{'last_edit_time'|load_lang}} : {{imp[3][1]}}
+                    {% endif %}
                     {% if menu != 0 %}
                         <div id="menu">
                             {% for menu_data in menu %}

+ 2 - 2
views/ringo/info.json

@@ -1,5 +1,5 @@
 {
     "name" : "Ringo",
-    "skin_ver" : "v1.0.3",
-    "require_ver" : "3500111"
+    "skin_ver" : "v1.0.4",
+    "require_ver" : "3500112"
 }