Browse Source

Merge pull request #1989 from openNAMU/dev

추가 패치
잉여개발기 2 years ago
parent
commit
4dbac67ef0

+ 44 - 26
app.py

@@ -43,16 +43,10 @@ with get_db_connect() as conn:
 
     if data_db_set['type'] == 'mysql':
         try:
-            curs.execute(db_change(
-                'create database ' + data_db_set['name'] + ' ' + \
-                'default character set utf8mb4'
-            ))
+            curs.execute(db_change('create database ' + data_db_set['name'] + ' default character set utf8mb4'))
         except:
             try:
-                curs.execute(db_change(
-                    'alter database ' + data_db_set['name'] + ' ' + \
-                    'character set utf8mb4'
-                ))
+                curs.execute(db_change('alter database ' + data_db_set['name'] + ' character set utf8mb4'))
             except:
                 pass
 
@@ -66,7 +60,6 @@ with get_db_connect() as conn:
 
                 try:
                     curs.execute(db_change('select ' + create + ' from ' + create_table + ' limit 1'))
-
                     db_pass = 1
                 except:
                     pass
@@ -74,7 +67,6 @@ with get_db_connect() as conn:
                 if db_pass == 0:
                     try:
                         curs.execute(db_change('create table ' + create_table + '(test longtext default (""))'))
-
                         db_pass = 1
                     except Exception as e:
                         # print(e)
@@ -83,7 +75,6 @@ with get_db_connect() as conn:
                 if db_pass == 0:
                     try:
                         curs.execute(db_change('create table ' + create_table + '(test longtext default "")'))
-
                         db_pass = 1
                     except Exception as e:
                         # print(e)
@@ -92,7 +83,6 @@ with get_db_connect() as conn:
                 if db_pass == 0:
                     try:
                         curs.execute(db_change('create table ' + create_table + '(test longtext)'))
-
                         db_pass = 1
                     except Exception as e:
                         # print(e)
@@ -101,7 +91,6 @@ with get_db_connect() as conn:
                 if db_pass == 0:
                     try:
                         curs.execute(db_change("alter table " + create_table + " add column " + create + " longtext default ('')"))
-
                         db_pass = 1
                     except Exception as e:
                         # print(e)
@@ -110,7 +99,6 @@ with get_db_connect() as conn:
                 if db_pass == 0:
                     try:
                         curs.execute(db_change("alter table " + create_table + " add column " + create + " longtext default ''"))
-
                         db_pass = 1
                     except Exception as e:
                         # print(e)
@@ -119,7 +107,6 @@ with get_db_connect() as conn:
                 if db_pass == 0:
                     try:
                         curs.execute(db_change("alter table " + create_table + " add column " + create + " longtext"))
-
                         db_pass = 1
                     except Exception as e:
                         # print(e)
@@ -216,37 +203,60 @@ with get_db_connect() as conn:
 
     # Init-DB_care
     if data_db_set['type'] == 'sqlite':
-        def back_up(back_time, back_up_where):
+        def back_up(back_time, back_up_where, back_up_count = 0):
             try:
+                if back_up_count != 0:
+                    file_dir = os.path.split(back_up_where)[0]
+                    file_dir = '.' if file_dir == '' else file_dir
+                    
+                    file_name = os.path.split(back_up_where)[1]
+                    file_name = re.sub(r'\.db$', '_[0-9]{14}.db', file_name)
+
+                    backup_file = [for_a for for_a in os.listdir(file_dir) if re.search('^' + file_name + '$', for_a)]
+                    backup_file = sorted(backup_file)
+                    
+                    if len(backup_file) >= back_up_count:
+                        remove_dir = os.path.join(file_dir, backup_file[0])
+                        os.remove(remove_dir)
+                        print('Back up : Remove (' + remove_dir + ')')
+
+                now_time = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
+                new_file_name = re.sub(r'\.db$', '_' + now_time + '.db', back_up_where)
                 shutil.copyfile(
                     data_db_set['name'] + '.db', 
-                    back_up_where.replace('%t', datetime.datetime.now().strftime('%Y-%m-%d_%H.%M.%S'))
+                    new_file_name
                 )
 
-                print('Back up : OK')
-            except:
+                print('Back up : OK (' + new_file_name + ')')
+            except Exception as e:
                 print('Back up : Error')
+                print(e)
 
             threading.Timer(
                 60 * 60 * back_time, 
                 back_up,
-                [back_time, back_up_where]
+                [back_time, back_up_where, back_up_count]
             ).start()
 
         curs.execute(db_change('select data from other where name = "back_up"'))
         back_time = curs.fetchall()
         back_time = float(number_check(back_time[0][0], True)) if back_time and back_time[0][0] != '' else 0
+
+        curs.execute(db_change('select data from other where name = "backup_count"'))
+        back_up_count = curs.fetchall()
+        back_up_count = int(number_check(back_up_count[0][0])) if back_up_count and back_up_count[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()
-            if back_up_where and back_up_where[0][0] != '':
-                back_up_where = back_up_where[0][0]
-            else:
-                back_up_where = 'back_' + data_db_set['name'] + '.db'
+            back_up_where = back_up_where[0][0] if back_up_where and back_up_where[0][0] != '' else data_db_set['name'] + '.db'
 
             print('Back up state : ' + str(back_time) + ' hours')
+            print('Back up directory : ' + back_up_where)
+            if back_up_count != 0:
+                print('Back up max number : ' + str(back_up_count))
 
-            back_up(back_time, back_up_where)
+            back_up(back_time, back_up_where, back_up_count)
         else:
             print('Back up state : Turn off')
 
@@ -280,12 +290,18 @@ app.route('/filter/document/del/<everything:name>', defaults = { 'tool' : 'docum
 
 app.route('/filter/edit_top', defaults = { 'tool' : 'edit_top' })(filter_all)
 app.route('/filter/edit_top/add', methods = ['POST', 'GET'], defaults = { 'tool' : 'edit_top' })(filter_all_add)
+app.route('/filter/edit_top/add/<everything:name>', methods = ['POST', 'GET'], defaults = { 'tool' : 'edit_top' })(filter_all_add)
 app.route('/filter/edit_top/del/<everything:name>', defaults = { 'tool' : 'edit_top' })(filter_all_delete)
 
 app.route('/filter/image_license', defaults = { 'tool' : 'image_license' })(filter_all)
 app.route('/filter/image_license/add', methods = ['POST', 'GET'], defaults = { 'tool' : 'image_license' })(filter_all_add)
 app.route('/filter/image_license/del/<everything:name>', defaults = { 'tool' : 'image_license' })(filter_all_delete)
 
+app.route('/filter/template', defaults = { 'tool' : 'template' })(filter_all)
+app.route('/filter/template/add', methods = ['POST', 'GET'], defaults = { 'tool' : 'template' })(filter_all_add)
+app.route('/filter/template/add/<everything:name>', methods = ['POST', 'GET'], defaults = { 'tool' : 'template' })(filter_all_add)
+app.route('/filter/template/del/<everything:name>', defaults = { 'tool' : 'template' })(filter_all_delete)
+
 app.route('/filter/edit_filter', defaults = { 'tool' : 'edit_filter' })(filter_all)
 app.route('/filter/edit_filter/add', methods = ['POST', 'GET'], defaults = { 'tool' : 'edit_filter' })(filter_all_add)
 app.route('/filter/edit_filter/add/<everything:name>', methods = ['POST', 'GET'], defaults = { 'tool' : 'edit_filter' })(filter_all_add)
@@ -331,11 +347,13 @@ app.route('/list/document/short/<int:arg_num>', defaults = { 'tool' : 'short_pag
 
 app.route('/list/file')(list_image_file)
 app.route('/list/file/<int:arg_num>')(list_image_file)
+app.route('/list/image', defaults = { 'do_type' : 1 })(list_image_file)
+app.route('/list/image/int:arg_num', defaults = { 'do_type' : 1 })(list_image_file)
 
 app.route('/list/admin')(list_admin)
 
 app.route('/list/admin/auth_use', methods = ['POST', 'GET'])(list_admin_auth_use)
-app.route('/list/admin/auth_use/<arg_search>/<int:arg_num>', methods = ['POST', 'GET'])(list_admin_auth_use)
+app.route('/list/admin/auth_use_page/<int:arg_num>/<everything:arg_search>', methods = ['POST', 'GET'])(list_admin_auth_use)
 
 app.route('/list/user')(list_user)
 app.route('/list/user/<int:arg_num>')(list_user)

+ 3 - 1
lang/en-US.json

@@ -55,7 +55,7 @@
         "close" : "Closed",
         "open" : "Open",
         "agreement" : "Agreement",
-        "template" : "Template",
+        "template" : "Template document",
         "file" : "File",
         "writer" : "Writer",
         "editor" : "Editor",
@@ -247,6 +247,7 @@
         "main_user_name" : "Main user name",
         "change_user_name" : "Change main user name",
         "sub_user_name" : "Sub user name",
+        "start_with_search" : "Search from the first letter",
         "_comment_" : "BBS",
             "bbs" : "BBS",
             "bbs_main" : "BBS main",
@@ -614,6 +615,7 @@
             "main_css_warning" : "If you have a problem using this, use the emergency tool.",
             "not_support_skin_warning" : "It does not work on skins that do not support this feature.",
             "old_page_warning" : "This page is out of date.",
+            "backup_warning" : "Warning. Files with the same name may be erased.",
         "_comment_" : "Challenge",
             "challenge_title_register" : "Hello, World!",
             "challenge_info_register" : "Sign up",

+ 4 - 2
lang/ko-KR.json

@@ -132,7 +132,7 @@
     "update_error": "자동 업데이트가 지원되지 않습니다.",
     "need_document": "필요한 문서들",
     "sub": "하위",
-    "template": "",
+    "template": "템플릿 문서",
     "user_document_acl": "사용자 문서 ACL",
     "password_confirm": "비밀번호 확인",
     "pinned": "고정",
@@ -575,5 +575,7 @@
     "math_scroll": "Math 매크로에서 스크롤 사용",
     "remove_blind_thread": "숨겨진 토론 보이지 않기",
     "trace": "추적",
-    "view_history": "문서 열람 추적 사용"
+    "view_history": "문서 열람 추적 사용",
+    "start_with_search" : "첫 글자부터 검색",
+    "backup_warning" : "경고. 동일한 이름의 파일이 있는 경우 지워질 수 있습니다."
 }

+ 6 - 0
route/edit.py

@@ -177,6 +177,12 @@ def edit(name = 'Test', section = 0, do_type = ''):
             else:
                 leng = '+' + str(len(content))
 
+            curs.execute(db_change("select data from other where name = 'document_content_max_length'"))
+            db_data_3 = curs.fetchall()
+            if db_data_3 and db_data_3[0][0] != '':
+                if int(number_check(db_data_3[0][0])) < len(content):
+                    return re_error('/error/44')
+
             curs.execute(db_change("select data from other where name = 'edit_timeout'"))
             db_data_2 = curs.fetchall()
             db_data_2 = '' if not db_data_2 else number_check(db_data_2[0][0])

+ 6 - 0
route/edit_revert.py

@@ -36,6 +36,12 @@ def edit_revert(name, num):
 
             if do_edit_filter(data[0][0]) == 1:
                 return re_error('/error/21')
+            
+            curs.execute(db_change("select data from other where name = 'document_content_max_length'"))
+            db_data = curs.fetchall()
+            if db_data and db_data[0][0] != '':
+                if int(number_check(db_data[0][0])) < len(data[0][0]):
+                    return re_error('/error/44')
 
             curs.execute(db_change("select data from data where title = ?"), [name])
             data_old = curs.fetchall()

+ 4 - 1
route/filter_all.py

@@ -42,6 +42,9 @@ def filter_all(tool):
         elif tool == 'outer_link':
             title = load_lang('outer_link_filter_list')
             curs.execute(db_change("select html, plus, plus_t from html_filter where kind = 'outer_link'"))
+        elif tool == 'template':
+            title = load_lang('template_document_list')
+            curs.execute(db_change("select html, plus, plus_t from html_filter where kind = 'template'"))
         else:
             title = load_lang('edit_tool_list')
             curs.execute(db_change("select html, plus, plus_t from html_filter where kind = 'edit_top'"))
@@ -53,7 +56,7 @@ def filter_all(tool):
 
             div += html.escape(data[0])
             if admin == 1:
-                if tool in ('inter_wiki', 'outer_link', 'edit_filter', 'document'):
+                if tool in ('inter_wiki', 'outer_link', 'edit_filter', 'document', 'edit_top', 'template'):
                     div += ' <a href="/filter/' + tool + '/add/' + url_pas(data[0]) + '">(' + load_lang('edit') + ')</a>'
                     
                 div += ' <a href="/filter/' + tool + '/del/' + url_pas(data[0]) + '">(' + load_lang('delete') + ')</a>'

+ 51 - 9
route/filter_all_add.py

@@ -15,9 +15,14 @@ def filter_all_add(tool, name = None):
             if tool in ('inter_wiki', 'outer_link'):
                 link = flask.request.form.get('link', 'test')
                 icon = flask.request.form.get('icon', '')
+                inter_type = flask.request.form.get('inter_type', '')
 
                 curs.execute(db_change("delete from html_filter where html = ? and kind = ?"), [title, tool])
                 curs.execute(db_change('insert into html_filter (html, plus, plus_t, kind) values (?, ?, ?, ?)'), [title, link, icon, tool])
+                if tool == 'inter_wiki':
+                    curs.execute(db_change("delete from html_filter where html = ? and kind = 'inter_wiki_sub'"), [title])
+                    curs.execute(db_change('insert into html_filter (html, plus, plus_t, kind) values (?, "inter_wiki_type", ?, "inter_wiki_sub")'), [title, inter_type])
+                
                 admin_check(None, tool + ' edit')
             elif tool == 'edit_filter':
                 sec = flask.request.form.get('second', '0')
@@ -73,6 +78,10 @@ def filter_all_add(tool, name = None):
                 elif tool == 'extension_filter':
                     admin_check(None, 'extension_filter edit')
                     type_d = 'extension'
+                elif tool == 'template':
+                    admin_check(None, 'template_document edit')
+                    type_d = 'template'
+                    plus_d = flask.request.form.get('exp', 'test')
                 else:
                     admin_check(None, 'edit_top edit')
                     type_d = 'edit_top'
@@ -98,8 +107,25 @@ def filter_all_add(tool, name = None):
                     exist = curs.fetchall()
                     value = exist[0] if exist else value
 
+                select = ''
                 if tool == 'inter_wiki':
                     ex = 'https://namu.wiki/w/'
+
+                    select = ['', '']
+                    curs.execute(db_change("select plus_t from html_filter where kind = 'inter_wiki_sub' and html = ?"), [name])
+                    db_data = curs.fetchall()
+                    if db_data and db_data[0][0] == 'under_bar':
+                        select = ['', 'selected']
+
+                    select = '''
+                        <hr class="main_hr">
+                        ''' + load_lang('inter_wiki_space_change') + '''
+                        <hr class="main_hr">
+                        <select name="inter_type">
+                            <option ''' + select[0] + ''' value="url_encode">%20</option>
+                            <option ''' + select[1] + ''' value="under_bar">_</option>
+                        </select>
+                    '''
                 else:
                     ex = 'youtube.com'
 
@@ -116,6 +142,7 @@ def filter_all_add(tool, name = None):
                     ''' + load_lang('icon') + ''' (''' + ('HTML' if tool == 'inter_wiki' else load_lang('html_or_link')) + ''') (''' + load_lang('link') + ' EX' + ''' : /image/Test.svg)
                     <hr class="main_hr">
                     <input value="''' + html.escape(value[2]) + '''" type="text" name="icon">
+                    ''' + select + '''
                 '''
             elif tool == 'edit_filter':            
                 curs.execute(db_change("select plus, plus_t from html_filter where html = ? and kind = 'regex_filter'"), [name])
@@ -137,12 +164,11 @@ def filter_all_add(tool, name = None):
                         ['31104000', load_lang('360_day')],
                         ['0', load_lang('limitless')]
                     ]
-                    for i in t_data:
-                        insert_data += '<a href="javascript:insert_v(\'second\', \'' + i[0] + '\')">(' + i[1] + ')</a> '
+                    insert_data += ''.join(['<a href="javascript:opennamu_insert_v(\'second\', \'' + for_a[0] + '\')">(' + for_a[1] + ')</a> ' for for_a in t_data])
 
                 title = load_lang('edit_filter_add')
                 form_data = '''
-                    <script>function insert_v(name, data) { document.getElementById(name).value = data; }</script>''' + insert_data + '''
+                    ''' + insert_data + '''
                     <hr class="main_hr">
                     <input placeholder="''' + load_lang('second') + '''" id="second" name="second" type="text" value="''' + html.escape(time_data) + '''">
                     <hr class="main_hr">
@@ -206,17 +232,33 @@ def filter_all_add(tool, name = None):
                         ''' + ''.join(['<option ' + for_a[0] + ' value=' + for_a[1] + '>' + ('normal' if for_a[1] == '' else for_a[1]) + '</option>' for for_a in acl_list]) + '''
                     </select>
                 '''
+            elif tool == 'template':
+                title = load_lang('template_document_add')
+
+                value = ''
+                if name:
+                    curs.execute(db_change("select plus from html_filter where html = ? and kind = 'template'"), [name])
+                    exist = curs.fetchall()
+                    value = exist[0][0] if exist else '' 
+
+                form_data = '' + \
+                    load_lang('template') + \
+                    '<hr class="main_hr">' + \
+                    '<input value="' + html.escape(name) + '" type="text" name="title">' + \
+                    '<hr class="main_hr">' + \
+                    load_lang('explanation') + \
+                    '<hr class="main_hr">' + \
+                    '<input value="' + html.escape(value) + '" type="text" name="exp">' + \
+                    '<hr class="main_hr">' + \
+                ''
             else:
                 title = load_lang('edit_tool_add')
+                
+                value = ''
                 if name:
                     curs.execute(db_change("select plus from html_filter where html = ? and kind = 'edit_top'"), [name])
                     exist = curs.fetchall()
-                    if exist:
-                        value = exist[0][0]
-                    else:
-                        value = ''
-                else:
-                    value = ''
+                    value = exist[0][0] if exist else ''    
 
                 form_data = '''
                     ''' + load_lang('title') + '''

+ 5 - 0
route/filter_all_delete.py

@@ -9,6 +9,7 @@ def filter_all_delete(tool, name = 'Test'):
 
         if tool == 'inter_wiki':
             curs.execute(db_change("delete from html_filter where html = ? and kind = 'inter_wiki'"), [name])
+            curs.execute(db_change("delete from html_filter where html = ? and kind = 'inter_wiki_sub'"), [name])
         elif tool == 'edit_filter':
             curs.execute(db_change("delete from html_filter where html = ? and kind = 'regex_filter'"), [name])
         elif tool == 'name_filter':
@@ -23,6 +24,10 @@ def filter_all_delete(tool, name = 'Test'):
             curs.execute(db_change("delete from html_filter where html = ? and kind = 'extension'"), [name])
         elif tool == 'document':
             curs.execute(db_change("delete from html_filter where html = ? and kind = 'document'"), [name])
+        elif tool == 'outer_link':
+            curs.execute(db_change("delete from html_filter where html = ? and kind = 'outer_link'"), [name])
+        elif tool == 'template':
+            curs.execute(db_change("delete from html_filter where html = ? and kind = 'template'"), [name])
         else:
             curs.execute(db_change("delete from html_filter where html = ? and kind = 'edit_top'"), [name])
 

+ 6 - 7
route/list_admin_auth_use.py

@@ -7,15 +7,14 @@ def list_admin_auth_use(arg_num = 1, arg_search = 'normal'):
         sql_num = (arg_num * 50 - 50) if arg_num * 50 > 0 else 0
 
         if flask.request.method == 'POST':
-            return redirect('/list/admin/auth_use/' + url_pas(flask.request.form.get('search', 'normal')) + '/1')
+            return redirect('/list/admin/auth_use_page/1/' + url_pas(flask.request.form.get('search', 'normal')))
         else:
+            arg_search = 'normal' if arg_search == '' else arg_search
+            
             if arg_search == 'normal':
                 curs.execute(db_change("select who, what, time from re_admin order by time desc limit ?, 50"), [sql_num])
             else:
-                curs.execute(
-                    db_change("select who, what, time from re_admin where what like ? order by time desc limit ?, 50"),
-                    [arg_search + "%", sql_num]
-                )
+                curs.execute(db_change("select who, what, time from re_admin where what like ? order by time desc limit ?, 50"), [arg_search + "%", sql_num])
 
             list_data = '<ul class="opennamu_ul">'
 
@@ -33,7 +32,7 @@ def list_admin_auth_use(arg_num = 1, arg_search = 'normal'):
                 list_data += '<li>' + ip_pas(data[0]) + ' | ' + html.escape(do_data) + ' | ' + data[2] + '</li>'
 
             list_data += '</ul>'
-            list_data += next_fix('/list/admin/auth_use/' + url_pas(arg_search) + '/', arg_num, get_list)
+            list_data += get_next_page_bottom('/list/admin/auth_use_page/{}/' + url_pas(arg_search), arg_num, get_list)
 
             arg_search = html.escape(arg_search) if arg_search != 'normal' else ''
 
@@ -41,7 +40,7 @@ def list_admin_auth_use(arg_num = 1, arg_search = 'normal'):
                 imp = [load_lang('authority_use_list'), wiki_set(), wiki_custom(), wiki_css([0, 0])],
                 data = '''
                     <form method="post">
-                        <input class="opennamu_width_200" name="search" value="''' + arg_search + '''">
+                        <input class="opennamu_width_200" name="search" placeholder="''' + load_lang('start_with_search') + '''" value="''' + arg_search + '''">
                         <button type="submit">''' + load_lang('search') + '''</button>
                     </form>
                     <hr class="main_hr">

+ 50 - 7
route/list_image_file.py

@@ -1,19 +1,62 @@
 from .tool.func import *
 
-def list_image_file(arg_num = 1):
+def list_image_file(arg_num = 1, do_type = 0):
     with get_db_connect() as conn:
         curs = conn.cursor()
 
         sql_num = (arg_num * 50 - 50) if arg_num * 50 > 0 else 0
 
-        list_data = '<ul class="opennamu_ul">'
+        list_data = ''
+        if do_type == 0:
+            list_data += '<a href="/list/image">(' + load_lang('image') + ')</a>'
+        else:
+            list_data += '<a href="/list/file">(' + load_lang('normal') + ')</a>'
+        
+        list_data += '<hr class="main_hr">'
 
-        curs.execute(db_change("select title from data where title like 'file:%' limit ?, 50"), [sql_num])
-        data_list = curs.fetchall()
-        for data in data_list:
-            list_data += '<li><a href="/w/' + url_pas(data[0]) + '">' + html.escape(data[0]) + '</a></li>'
+        if do_type == 1:
+            render_data = ''
+            sub_data = ''
+            count = 0
 
-        list_data += next_fix('/list/file/', arg_num, data_list)
+            curs.execute(db_change("select title from data where title like 'file:%' limit ?, 50"), [sql_num])
+            data_list = curs.fetchall()
+            for data in data_list:
+                if count != 0 and count % 4 == 0:
+                    render_data += '||\n'
+                    render_data += sub_data + '||\n'
+                    
+                    sub_data = ''
+
+                render_data += '|| [[' + data[0] + ']] '
+                sub_data += '|| [[:' + data[0] + ']] '
+                count += 1
+
+            if render_data != '':
+                render_data += '||\n'
+                render_data += sub_data + '||'
+
+            end_data = render_set(
+                doc_name = '',
+                doc_data = render_data,
+                data_type = 'view',
+                markup = 'namumark'
+            )
+            list_data += end_data
+        else:
+            list_data += '<ul class="opennamu_ul">'
+
+            curs.execute(db_change("select title from data where title like 'file:%' limit ?, 50"), [sql_num])
+            data_list = curs.fetchall()
+            for data in data_list:
+                list_data += '<li><a href="/w/' + url_pas(data[0]) + '">' + html.escape(data[0]) + '</a></li>'
+
+            list_data += '</ul>'
+
+        if do_type == 0:
+            list_data += next_fix('/list/file/', arg_num, data_list)
+        else:
+            list_data += next_fix('/list/image/', arg_num, data_list)
 
         return easy_minify(flask.render_template(skin_check(),
             imp = [load_lang('image_file_list'), wiki_set(), wiki_custom(), wiki_css([0, 0])],

+ 13 - 17
route/login_login.py

@@ -22,27 +22,23 @@ def login_login_2():
 
             user_agent = flask.request.headers.get('User-Agent', '')
             user_id = flask.request.form.get('id', '')
-            user_data = {}
+            user_pw = flask.request.form.get('pw', '')
 
-            curs.execute(db_change(
-                'select name, data from user_set where id = ? and (name = "pw" or name = "encode")'
-            ), [user_id])
-            sql_data = curs.fetchall()
-            if not sql_data:
+            curs.execute(db_change("select data from user_set where id = ? and name = 'pw'"), [user_id])
+            db_data = curs.fetchall()
+            if not db_data:
                 return re_error('/error/2')
-
-            for i in sql_data:
-                user_data[i[0]] = i[1]
-
-            if len(user_data) < 2:
+            else:
+                db_user_pw = db_data[0][0]
+                
+            curs.execute(db_change("select data from user_set where id = ? and name = 'encode'"), [user_id])
+            db_data = curs.fetchall()
+            if not db_data:
                 return re_error('/error/2')
+            else:
+                db_user_encode = db_data[0][0]
 
-            if pw_check(
-                flask.request.form.get('pw', ''),
-                user_data['pw'],
-                user_data['encode'],
-                user_id
-            ) != 1:
+            if pw_check(user_pw, db_user_pw, db_user_encode, user_id) != 1:
                 if not 'login_count' in flask.session:
                     flask.session['login_count'] = 1
                 else:

+ 2 - 6
route/login_login_2fa.py

@@ -25,6 +25,7 @@ def login_login_2fa_2():
 
             user_agent = flask.request.headers.get('User-Agent', '')
             user_id = flask.session['login_id']
+            user_pw = flask.request.form.get('pw', '')
 
             curs.execute(db_change('select data from user_set where name = "2fa_pw" and id = ?'), [user_id])
             user_1 = curs.fetchall()
@@ -33,12 +34,7 @@ def login_login_2fa_2():
                 user_1 = user_1[0][0]
                 user_2 = curs.fetchall()[0][0]
 
-                pw_check_d = pw_check(
-                    flask.request.form.get('pw', ''),
-                    user_1,
-                    user_2,
-                    user_id
-                )
+                pw_check_d = pw_check(user_pw, user_1, user_2, user_id)
                 if pw_check_d != 1:
                     return re_error('/error/10')
 

+ 2 - 6
route/login_login_2fa_email.py

@@ -25,6 +25,7 @@ def login_login_2fa_email_2():
 
             user_agent = flask.request.headers.get('User-Agent', '')
             user_id = flask.session['b_id']
+            user_pw = flask.request.form.get('pw', '')
 
             curs.execute(db_change('select data from user_set where name = "2fa_pw" and id = ?'), [user_id])
             user_1 = curs.fetchall()
@@ -33,12 +34,7 @@ def login_login_2fa_email_2():
                 user_1 = user_1[0][0]
                 user_2 = curs.fetchall()[0][0]
 
-                pw_check_d = pw_check(
-                    flask.request.form.get('pw', ''),
-                    user_1,
-                    user_2,
-                    user_id
-                )
+                pw_check_d = pw_check(user_pw, user_1, user_2, user_id)
                 if pw_check_d != 1:
                     return re_error('/error/10')
 

+ 17 - 1
route/main_setting_main.py

@@ -41,6 +41,8 @@ def main_setting_main(db_set):
             37 : ['move_with_redirect', ''],
             38 : ['slow_thread', ''],
             39 : ['edit_timeout', '5'],
+            40 : ['document_content_max_length', ''],
+            41 : ['backup_count', '']
         }
 
         if flask.request.method == 'POST':
@@ -208,7 +210,11 @@ def main_setting_main(db_set):
                         <select name="update">''' + branch_div + '''</select>
 
                         <span ''' + sqlite_only + '''>
-                            <h3>''' + load_lang('sqlite_only') + '''</h3>
+                            <h3>''' + load_lang('backup') + ''' (''' + load_lang('sqlite_only') + ''')</h3>
+                            <span>''' + load_lang('backup_warning') + ''' (EX : data_YYYYMMDDHHMMSS.db)</span>
+                            <hr class="main_hr">
+                            <hr class="main_hr">
+                            
                             <span>''' + load_lang('backup_interval') + '''</span> (''' + load_lang('hour') + ''') (''' + load_lang('off') + ''' : ''' + load_lang('empty') + ''') (''' + load_lang('restart_required') + ''')
                             <hr class="main_hr">
                             <input name="back_up" value="''' + html.escape(d_list[9]) + '''">
@@ -218,6 +224,11 @@ def main_setting_main(db_set):
                             <hr class="main_hr">
                             <input name="backup_where" value="''' + html.escape(d_list[21]) + '''">
                             <hr class="main_hr">
+
+                            <span>''' + load_lang('backup_count') + '''</span> (''' + load_lang('default') + ''' : ''' + load_lang('empty') + ''') (''' + load_lang('restart_required') + ''')
+                            <hr class="main_hr">
+                            <input name="backup_count" value="''' + html.escape(d_list[41]) + '''">
+                            <hr class="main_hr">
                         </span>
 
                         <h2>''' + load_lang('edit_set') + '''</h2>
@@ -260,6 +271,11 @@ def main_setting_main(db_set):
                         <input name="edit_timeout" value="''' + html.escape(d_list[39]) + '''">
                         <hr class="main_hr">
 
+                        <span>''' + load_lang('document_content_max_length') + '''</span> (''' + load_lang('off') + ''' : ''' + load_lang('empty') + ''')
+                        <hr class="main_hr">
+                        <input name="document_content_max_length" value="''' + html.escape(d_list[40]) + '''">
+                        <hr class="main_hr">
+
                         <button id="opennamu_save_button" type="submit">''' + load_lang('save') + '''</button>
                     </form>
                 '''),

+ 11 - 1
route/main_setting_phrase.py

@@ -32,7 +32,9 @@ def main_setting_phrase():
             'bbs_help',
             'bbs_comment_help',
             'outdated_doc_warning',
-            'outdated_doc_warning_date'
+            'outdated_doc_warning_date',
+            'category_text',
+            'redirect_text'
         ]
         if flask.request.method == 'POST':
             curs.executemany(db_change("update other set data = ? where name = ?"), [[flask.request.form.get(for_a, ''), for_a] for for_a in i_list])
@@ -136,6 +138,14 @@ def main_setting_phrase():
                         <hr class="main_hr">
                         <textarea class="opennamu_textarea_100" name="''' + i_list[23] + '''" placeholder="''' + load_lang('old_page_warning') + '''">''' + html.escape(d_list[23]) + '''</textarea>
 
+                        <h2>''' + load_lang('category') + '''</h2>
+                        <input name="''' + i_list[25] + '''" value="''' + html.escape(d_list[25]) + '''">
+
+                        <h2>''' + load_lang('redirect') + '''</h2>
+                        <span>EX : {0} ➤ {1}</span>
+                        <hr class="main_hr">
+                        <input name="''' + i_list[26] + '''" value="''' + html.escape(d_list[26]) + '''">
+
                         <hr class="main_hr">
                         <button id="opennamu_save_button" type="submit">''' + load_lang('save') + '''</button>
                     </form>

+ 1 - 0
route/main_tool_admin.py

@@ -35,6 +35,7 @@ def main_tool_admin():
                     <li><a href="/filter/extension_filter">''' + load_lang('extension_filter_list') + '''</a></li>
                     <li><a href="/filter/document">''' + load_lang('document_filter_list') + '''</a></li>
                     <li><a href="/filter/outer_link">''' + load_lang('outer_link_filter_list') + '''</a> (''' + load_lang('beta') + ''')
+                    <li><a href="/filter/template">''' + load_lang('template_document_list') + '''</a> (''' + load_lang('beta') + ''')
                 </ul>
                 <h3>''' + load_lang('server') + '''</h2>
                 <ul class="opennamu_ul">

+ 14 - 1
route/main_tool_redirect.py

@@ -2,6 +2,8 @@ from .tool.func import *
 
 def main_tool_redirect(num = 1, add_2 = ''):
     with get_db_connect() as conn:
+        curs = conn.cursor()
+
         title_list = {
             0 : [load_lang('document_name'), '/acl', load_lang('acl')],
             1 : [0, '/list/user/check', load_lang('check')],
@@ -49,11 +51,22 @@ def main_tool_redirect(num = 1, add_2 = ''):
                 if num == 15:
                     plus = '<input type="checkbox" name="regex"> ' + load_lang('regex') + '<hr class="main_hr">'
 
+                top_plus = ''
+                if num == 13:
+                    curs.execute(db_change("select html, plus from html_filter where kind = 'template'"))
+                    db_data = curs.fetchall()
+                    for for_a in db_data:
+                        top_plus += '' + \
+                            '<a href="javascript:opennamu_insert_v(\'data_field\', \'' + get_tool_js_safe(for_a[0]) + '\')">' + html.escape(for_a[0]) + '</a> : ' + html.escape(for_a[1]) + \
+                            '<hr class="main_hr">' + \
+                        ''
+
                 return easy_minify(flask.render_template(skin_check(),
                     imp = [title_list[num][2], wiki_set(), wiki_custom(), wiki_css([0, 0])],
                     data = '''
                         <form method="post">
-                            <input placeholder="''' + placeholder + '''" name="name" type="text">
+                            ''' + top_plus + '''
+                            <input placeholder="''' + placeholder + '''" id="data_field" name="name" type="text">
                             <hr class="main_hr">
                             ''' + plus + '''
                             <button type="submit">''' + load_lang('go') + '''</button>

+ 1 - 0
route/main_view_image.py

@@ -3,6 +3,7 @@ from .main_func_error_404 import main_func_error_404
 
 def main_view_image(name = ''):
     with get_db_connect() as conn:
+        name = re.sub(r'\.cache_v(?:[0-9]+)$', '', name)
         mime_type = re.search(r'([^.]+)$', name)
         if mime_type:
             mime_type = mime_type.group(1).lower()

+ 19 - 22
route/tool/func.py

@@ -899,24 +899,15 @@ def pw_check(data, data2, type_d = 'no', id_d = ''):
         curs.execute(db_change('select data from other where name = "encode"'))
         db_data = curs.fetchall()
         load_set_data = db_data[0][0] if db_data and db_data[0][0] != '' else 'sha3'
-        set_data = db_data[0][0] if db_data and db_data[0][0] != '' else 'sha3'
         
+        set_data = load_set_data
         if type_d != 'no':
-            if type_d == '':
-                set_data = 'sha3'
-            else:
-                set_data = type_d
+            set_data = 'sha3' if type_d == '' else type_d
 
         re_data = 1 if pw_encode(data, set_data) == data2 else 0
         if load_set_data != set_data and re_data == 1 and id_d != '':
-            curs.execute(db_change("update user_set set data = ? where id = ? and name = 'pw'"), [
-                pw_encode(data), 
-                id_d
-            ])
-            curs.execute(db_change("update user_set set data = ? where id = ? and name = 'encode'"), [
-                load_set_data, 
-                id_d
-            ])
+            curs.execute(db_change("update user_set set data = ? where id = ? and name = 'pw'"), [pw_encode(data), id_d])
+            curs.execute(db_change("update user_set set data = ? where id = ? and name = 'encode'"), [load_set_data, id_d])
 
         return re_data
         
@@ -968,10 +959,7 @@ def load_lang(data, safe = 0):
         else:
             lang_list = os.listdir('lang')
             if (lang_name + '.json') in lang_list:
-                lang = json.loads(open(
-                    os.path.join('lang', lang_name + '.json'), 
-                    encoding = 'utf8'
-                ).read())
+                lang = json.loads(open(os.path.join('lang', lang_name + '.json'), encoding = 'utf8').read())
                 global_lang[lang_name] = lang
             else:
                 lang = {}
@@ -1028,7 +1016,7 @@ def wiki_css(data):
     data_css = ''
     data_css_add = ''
 
-    data_css_ver = '184'
+    data_css_ver = '185'
     data_css_ver = '.cache_v' + data_css_ver
 
     if 'main_css' in global_wiki_set:
@@ -1295,7 +1283,7 @@ def load_skin(data = '', set_n = 0, default = 0):
     return skin_return_data
 
 # Func-markup
-def render_set(doc_name = '', doc_data = '', data_type = 'view', data_in = '', doc_acl = ''):
+def render_set(doc_name = '', doc_data = '', data_type = 'view', data_in = '', doc_acl = '', markup = ''):
     with get_db_connect() as conn:
         curs = conn.cursor()
 
@@ -1326,7 +1314,12 @@ def render_set(doc_name = '', doc_data = '', data_type = 'view', data_in = '', d
                     'category' : load_lang('category')
                 }
 
-                get_class_render = class_do_render(conn, render_lang_data).do_render(doc_name, doc_data, data_type, data_in)
+                curs.execute(db_change('select data from other where name = "category_text"'))
+                db_data = curs.fetchall()
+                if db_data and db_data[0][0] != '':
+                    render_lang_data['category'] = db_data[0][0]
+
+                get_class_render = class_do_render(conn, render_lang_data, markup).do_render(doc_name, doc_data, data_type, data_in)
                 
                 if 'include' in get_class_render[2]:
                     for_a = 0
@@ -1341,7 +1334,7 @@ def render_set(doc_name = '', doc_data = '', data_type = 'view', data_in = '', d
                         if acl_result == 0:
                             include_regex = re.compile('<div id="' + include_data[0] + '"><\\/div>')
                             if re.search(include_regex, get_class_render[0]):
-                                include_data_render = class_do_render(conn, render_lang_data).do_render(include_data[1], include_data[2], data_type, include_data[0] + data_in)
+                                include_data_render = class_do_render(conn, render_lang_data, markup).do_render(include_data[1], include_data[2], data_type, include_data[0] + data_in)
                                 if len(include_data) > 3:
                                     include_data_render[0] = '<div id="' + include_data[0] + '" ' + include_data[3] + '>' + include_data_render[0] + '</div>'
                                 else:
@@ -2653,8 +2646,12 @@ def re_error(data):
             elif num == 43:
                 title = load_lang('application_submitted')
                 sub_title = title
-
                 data = load_lang('waiting_for_approval')
+            elif num == 44:
+                curs.execute(db_change("select data from other where name = 'document_content_max_length'"))
+                db_data = curs.fetchall()
+                db_data = '' if not db_data else db_data[0][0]
+                data = load_lang('error_content_length_too_long') + db_data
             else:
                 data = '???'
 

+ 3 - 2
route/tool/func_render.py

@@ -5,7 +5,7 @@ from .func_render_namumark import class_do_render_namumark
 # 커스텀 마크 언젠간 다시 추가 예정
 
 class class_do_render:
-    def __init__(self, conn, lang_data = {}):
+    def __init__(self, conn, lang_data = {}, markup = ''):
         self.conn = conn
 
         if lang_data == '{}':
@@ -15,6 +15,7 @@ class class_do_render:
             }
 
         self.lang_data = lang_data
+        self.markup = markup
 
     def do_render(self, doc_name, doc_data, data_type, data_in):
         curs = self.conn.cursor()
@@ -26,7 +27,7 @@ class class_do_render:
         
         data_in = (data_in + '_') if data_in != '' else ''
         doc_set['doc_include'] = data_in
-        rep_data = ''
+        rep_data = self.markup
 
         if rep_data == '' and doc_name != '':
             curs.execute(db_change("select set_data from data_set where doc_name = ? and set_name = 'document_markup'"), [doc_name])

+ 15 - 1
route/tool/func_render_namumark.py

@@ -896,6 +896,10 @@ class class_do_render_namumark:
                             link_exist = 'opennamu_not_exist_link'
                             self.data_backlink['file:' + link_main]['no'] = ''
 
+                        self.curs.execute(db_change('select id from history where title = ? order by date desc limit 1'), ['file:' + link_main])
+                        db_data = self.curs.fetchall()
+                        rev = db_data[0][0] if db_data else '1' 
+
                         self.data_backlink['file:' + link_main]['file'] = ''
 
                         link_extension_regex = r'\.([^.]+)$'
@@ -908,7 +912,7 @@ class class_do_render_namumark:
                         link_main = re.sub(link_extension_regex, '', link_main)
                         link_main_org = link_main
 
-                        link_main = '/image/' + url_pas(sha224_replace(link_main)) + '.' + link_extension
+                        link_main = '/image/' + url_pas(sha224_replace(link_main)) + '.' + link_extension + '.cache_v' + rev
 
                     if file_width != '':
                         file_width = 'width:' + self.get_tool_css_safe(file_width) + ';'
@@ -1079,6 +1083,11 @@ class class_do_render_namumark:
 
                         link_sub_storage = link_inter_icon + link_sub_storage
 
+                        self.curs.execute(db_change("select plus_t from html_filter where kind = 'inter_wiki_sub' and html = ?"), [link_inter_name])
+                        db_data = self.curs.fetchall()
+                        if db_data and db_data[0][0] == 'under_bar':
+                            link_main = link_main.replace('%20', '_')
+
                         data_name = self.get_tool_data_storage('<a class="opennamu_link_inter" title="' + link_title + '" href="' + link_main + link_data_sharp + '">' + link_sub_storage, '</a>', link_data_full)
                     
                         self.render_data = re.sub(link_regex, lambda x : ('<' + data_name + '>' + link_sub + '</' + data_name + '>'), self.render_data, 1)
@@ -1550,6 +1559,11 @@ class class_do_render_namumark:
 
                     link_sub_storage = link_inter_icon + link_sub_storage
 
+                    self.curs.execute(db_change("select plus_t from html_filter where kind = 'inter_wiki_sub' and html = ?"), [link_inter_name])
+                    db_data = self.curs.fetchall()
+                    if db_data and db_data[0][0] == 'under_bar':
+                        link_main = link_main.replace('%20', '_')
+
                     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)

+ 1 - 1
route/view_acl.py

@@ -185,6 +185,6 @@ def view_acl(name):
                 menu = [
                     ['w/' + url_pas(name), load_lang('return')], 
                     ['manager', load_lang('admin')], 
-                    ['list/admin/auth_use/' + url_pas('acl (' + name + ')') + '/1', load_lang('acl_record')]
+                    ['list/admin/auth_use_page/1/' + url_pas('acl (' + name + ')'), load_lang('acl_record')]
                 ]
             ))

+ 19 - 2
route/view_read.py

@@ -116,6 +116,10 @@ def view_read(name = 'Test', doc_rev = '', doc_from = '', do_type = ''):
             if name == 'user:' + user_name:
                 menu += [['w/' + url_pas(name) + '/' + url_pas(now_time.split()[0]), load_lang('today_doc')]]
         elif re.search(r"^file:", name):
+            curs.execute(db_change('select id from history where title = ? order by date desc limit 1'), [name])
+            db_data = curs.fetchall()
+            rev = db_data[0][0] if db_data else '1' 
+
             name_view = name
             doc_type = 'file'
 
@@ -133,7 +137,7 @@ def view_read(name = 'Test', doc_rev = '', doc_from = '', do_type = ''):
             if os.path.exists(file_path_name):
                 file_size = str(round(os.path.getsize(file_path_name) / 1000, 1))
                 file_data = '''
-                    <img src="/image/''' + url_pas(file_all_name) + '''">
+                    <img src="/image/''' + url_pas(file_all_name) + '''.cache_v''' + rev + '''">
                     <h2>''' + load_lang('data') + '''</h2>
                     <table>
                         <tr><td>URL</td><td><a href="/image/''' + url_pas(file_all_name) + '''">''' + load_lang('link') + '''</a></td></tr>
@@ -242,9 +246,22 @@ def view_read(name = 'Test', doc_rev = '', doc_from = '', do_type = ''):
                     if curs.fetchall():
                         break
 
+                redirect_text = '{0} ➤ {1}'
+
+                curs.execute(db_change('select data from other where name = "redirect_text"'))
+                db_data = curs.fetchall()
+                if db_data and db_data[0][0] != '':
+                    redirect_text = db_data[0][0]
+
+                try:
+                    redirect_text = redirect_text.format('<a href="/w_from/' + url_pas(last_page) + '">' + html.escape(last_page) + '</a>', '<b>' + html.escape(name) + '</b>')
+                except:
+                    redirect_text = '{0} ➤ {1}'
+                    redirect_text = redirect_text.format('<a href="/w_from/' + url_pas(last_page) + '">' + html.escape(last_page) + '</a>', '<b>' + html.escape(name) + '</b>')
+
                 end_data = '''
                     <div id="redirect">
-                        <a href="/w_from/''' + url_pas(last_page) + '''">''' + html.escape(last_page) + '''</a> ➤ <b>''' + html.escape(name) + '''</b>
+                        ''' + redirect_text + '''
                     </div>
                     <hr class="main_hr">
                 ''' + end_data

+ 1 - 1
version.json

@@ -1,6 +1,6 @@
 {
     "beta" : {
-        "r_ver" : "v3.4.6-RC5-dev38",
+        "r_ver" : "v3.4.6-RC5-dev47",
         "c_ver" : "3500373",
         "s_ver" : "3500112"
     }

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

@@ -154,4 +154,8 @@ function opennamu_get_main_skin_set(set_name) {
             }
         }
     });
+}
+
+function opennamu_insert_v(name, data) {
+    document.getElementById(name).value = data;
 }