|
|
@@ -17,7 +17,7 @@ import random
|
|
|
import sys
|
|
|
|
|
|
# 버전 표기
|
|
|
-r_ver = 'v3.0.2'
|
|
|
+r_ver = 'v3.0.3-Beta-03'
|
|
|
print('Version : ' + r_ver)
|
|
|
|
|
|
# 나머지 불러오기
|
|
|
@@ -176,13 +176,6 @@ lang_data = json.loads(json_data)
|
|
|
print('')
|
|
|
|
|
|
# 호환성 설정
|
|
|
-try:
|
|
|
- curs.execute("drop table move")
|
|
|
-
|
|
|
- print('move table delete')
|
|
|
-except:
|
|
|
- pass
|
|
|
-
|
|
|
try:
|
|
|
curs.execute("alter table history add hide text default ''")
|
|
|
|
|
|
@@ -731,7 +724,7 @@ def admin_plus(name = None):
|
|
|
data += '<li><input type="checkbox" ' + state + ' name="check" ' + exist_list[3] + '> 사용자 검사</li>'
|
|
|
data += '<li><input type="checkbox" ' + state + ' name="acl" ' + exist_list[4] + '> ' + lang_data['document'] + ' ACL</li>'
|
|
|
data += '<li><input type="checkbox" ' + state + ' name="hidel" ' + exist_list[5] + '> ' + lang_data['history'] + ' ' + lang_data['hide'] + '</li>'
|
|
|
- data += '<li><input type="checkbox" ' + state + ' name="give" ' + exist_list[6] + '> 권한 부여</li>'
|
|
|
+ data += '<li><input type="checkbox" ' + state + ' name="give" ' + exist_list[6] + '> 권한 관리</li>'
|
|
|
data += '<li><input type="checkbox" ' + state + ' name="owner" ' + exist_list[7] + '> 소유자</li></ul>'
|
|
|
|
|
|
return html_minify(render_template(skin_check(conn),
|
|
|
@@ -1269,26 +1262,22 @@ def raw_view(name = None, sub_title = None, num = None):
|
|
|
return redirect('/w/' + url_pas(name))
|
|
|
|
|
|
@app.route('/revert/<path:name>', methods=['POST', 'GET'])
|
|
|
-def revert(name = None):
|
|
|
- ip = ip_check()
|
|
|
- can = acl_check(conn, name)
|
|
|
- today = get_time()
|
|
|
-
|
|
|
+def revert(name = None):
|
|
|
num = int(request.args.get('num', 0))
|
|
|
|
|
|
+ curs.execute("select title from history where title = ? and id = ? and hide = 'O'", [name, str(num)])
|
|
|
+ if curs.fetchall() and admin_check(conn, 6, None) != 1:
|
|
|
+ return re_error(conn, '/error/3')
|
|
|
+
|
|
|
+ if acl_check(conn, name) == 1:
|
|
|
+ return re_error(conn, '/ban')
|
|
|
+
|
|
|
if request.method == 'POST':
|
|
|
if captcha_post(request.form.get('g-recaptcha-response', None), conn) == 1:
|
|
|
return re_error(conn, '/error/13')
|
|
|
else:
|
|
|
captcha_post('', conn, 0)
|
|
|
|
|
|
- curs.execute("select title from history where title = ? and id = ? and hide = 'O'", [name, str(num)])
|
|
|
- if curs.fetchall() and admin_check(conn, 6, None) != 1:
|
|
|
- return re_error(conn, '/error/3')
|
|
|
-
|
|
|
- if can == 1:
|
|
|
- return re_error(conn, '/ban')
|
|
|
-
|
|
|
curs.execute("delete from back where link = ?", [name])
|
|
|
conn.commit()
|
|
|
|
|
|
@@ -1304,20 +1293,13 @@ def revert(name = None):
|
|
|
leng = '+' + str(len(data[0][0]))
|
|
|
curs.execute("insert into data (title, data) values (?, ?)", [name, data[0][0]])
|
|
|
|
|
|
- history_plus(conn, name, data[0][0], today, ip, request.form['send'] + ' (' + str(num) + lang_data['version'] + ')', leng)
|
|
|
+ history_plus(conn, name, data[0][0], get_time(), ip_check(), request.form['send'] + ' (' + str(num) + lang_data['version'] + ')', leng)
|
|
|
namumark(conn, name, data[0][0], 1)
|
|
|
|
|
|
conn.commit()
|
|
|
|
|
|
return redirect('/w/' + url_pas(name))
|
|
|
else:
|
|
|
- curs.execute("select title from history where title = ? and id = ? and hide = 'O'", [name, str(num)])
|
|
|
- if curs.fetchall() and admin_check(conn, 6, None) != 1:
|
|
|
- return re_error(conn, '/error/3')
|
|
|
-
|
|
|
- if can == 1:
|
|
|
- return re_error(conn, '/ban')
|
|
|
-
|
|
|
curs.execute("select title from history where title = ? and id = ?", [name, str(num)])
|
|
|
if not curs.fetchall():
|
|
|
return redirect('/w/' + url_pas(name))
|
|
|
@@ -1566,9 +1548,14 @@ def edit(name = None):
|
|
|
data = get_data[0][0]
|
|
|
get_name = ''
|
|
|
|
|
|
+ # https://stackoverflow.com/questions/11076975/insert-text-into-textarea-at-cursor-position-javascript
|
|
|
+ js = '<script>function insertAtCursor(myField, myValue) { if (document.selection) { document.getElementById(myField).focus(); sel = document.selection.createRange(); sel.text = myValue; } else if (document.getElementById(myField).selectionStart || document.getElementById(myField).selectionStart == \'0\') { var startPos = document.getElementById(myField).selectionStart; var endPos = document.getElementById(myField).selectionEnd; document.getElementById(myField).value = document.getElementById(myField).value.substring(0, startPos) + myValue + document.getElementById(myField).value.substring(endPos, document.getElementById(myField).value.length); } else { document.getElementById(myField).value += myValue; }}</script>'
|
|
|
+
|
|
|
+ js_button = '<a href="javascript:void(0);" onclick="insertAtCursor(\'content\', \'[[]]\');">(링크)</a> <a href="javascript:void(0);" onclick="insertAtCursor(\'content\', \'[macro()]\');">(매크로)</a> <a href="javascript:void(0);" onclick="insertAtCursor(\'content\', \'{{{#! }}}\');">(중괄호)</a>'
|
|
|
+
|
|
|
return html_minify(render_template(skin_check(conn),
|
|
|
imp = [name, wiki_set(conn, 1), custom(conn), other2([' (수정)', 0])],
|
|
|
- data = get_name + '<form method="post" action="/edit/' + url_pas(name) + action + '"><textarea rows="25" name="content">' + html.escape(re.sub('\n$', '', data)) + '</textarea><textarea style="display: none;" name="otent">' + html.escape(re.sub('\n$', '', data_old)) + '</textarea><hr><input placeholder="사유" name="send" type="text"><hr>' + captcha_get(conn) + '' + ip_warring(conn) + '<button id="save" type="submit">저장</button><button id="preview" type="submit" formaction="/preview/' + url_pas(name) + action + '">미리보기</button></form>',
|
|
|
+ data = get_name + js + '<form method="post" action="/edit/' + url_pas(name) + action + '">' + js_button + '<hr><textarea id="content" rows="25" name="content">' + html.escape(re.sub('\n$', '', data)) + '</textarea><textarea style="display: none;" name="otent">' + html.escape(re.sub('\n$', '', data_old)) + '</textarea><hr><input placeholder="사유" name="send" type="text"><hr>' + captcha_get(conn) + '' + ip_warring(conn) + '<button id="save" type="submit">저장</button><button id="preview" type="submit" formaction="/preview/' + url_pas(name) + action + '">미리보기</button></form>',
|
|
|
menu = [['w/' + url_pas(name), lang_data['document']], ['delete/' + url_pas(name), '삭제'], ['move/' + url_pas(name), lang_data['move']]]
|
|
|
))
|
|
|
|
|
|
@@ -1593,9 +1580,14 @@ def preview(name = None):
|
|
|
else:
|
|
|
action = ''
|
|
|
|
|
|
+ # https://stackoverflow.com/questions/11076975/insert-text-into-textarea-at-cursor-position-javascript
|
|
|
+ js = '<script>function insertAtCursor(myField, myValue) { if (document.selection) { document.getElementById(myField).focus(); sel = document.selection.createRange(); sel.text = myValue; } else if (document.getElementById(myField).selectionStart || document.getElementById(myField).selectionStart == \'0\') { var startPos = document.getElementById(myField).selectionStart; var endPos = document.getElementById(myField).selectionEnd; document.getElementById(myField).value = document.getElementById(myField).value.substring(0, startPos) + myValue + document.getElementById(myField).value.substring(endPos, document.getElementById(myField).value.length); } else { document.getElementById(myField).value += myValue; }}</script>'
|
|
|
+
|
|
|
+ js_button = '<a href="javascript:void(0);" onclick="insertAtCursor(\'content\', \'[[]]\');">(링크)</a> <a href="javascript:void(0);" onclick="insertAtCursor(\'content\', \'[macro()]\');">(매크로)</a> <a href="javascript:void(0);" onclick="insertAtCursor(\'content\', \'{{{#! }}}\');">(중괄호)</a>'
|
|
|
+
|
|
|
return html_minify(render_template(skin_check(conn),
|
|
|
imp = [name, wiki_set(conn, 1), custom(conn), other2([' (미리보기)', 0])],
|
|
|
- data = '<form method="post" action="/edit/' + url_pas(name) + action + '"><textarea rows="25" name="content">' + html.escape(request.form['content']) + '</textarea><textarea style="display: none;" name="otent">' + html.escape(request.form['otent']) + '</textarea><hr><input placeholder="사유" name="send" type="text"><hr>' + captcha_get(conn) + '<button id="save" type="submit">저장</button><button id="preview" type="submit" formaction="/preview/' + url_pas(name) + action + '">미리보기</button></form><hr>' + end_data,
|
|
|
+ data = js + '<form method="post" action="/edit/' + url_pas(name) + action + '">' + js_button + '<hr><textarea id="content" rows="25" name="content">' + html.escape(request.form['content']) + '</textarea><textarea style="display: none;" name="otent">' + html.escape(request.form['otent']) + '</textarea><hr><input placeholder="사유" name="send" type="text"><hr>' + captcha_get(conn) + '<button id="save" type="submit">저장</button><button id="preview" type="submit" formaction="/preview/' + url_pas(name) + action + '">미리보기</button></form><hr>' + end_data,
|
|
|
menu = [['w/' + url_pas(name), lang_data['document']]]
|
|
|
))
|
|
|
|
|
|
@@ -1955,7 +1947,7 @@ def topic(name = None, sub = None):
|
|
|
ip = ip_check()
|
|
|
today = get_time()
|
|
|
|
|
|
- if ban == 1 and admin != 1:
|
|
|
+ if ban == 1:
|
|
|
return re_error(conn, '/ban')
|
|
|
|
|
|
curs.execute("select id from topic where title = ? and sub = ? order by id + 0 desc limit 1", [name, sub])
|
|
|
@@ -2511,82 +2503,87 @@ def user_ban(name = None):
|
|
|
|
|
|
@app.route('/acl/<path:name>', methods=['POST', 'GET'])
|
|
|
def acl(name = None):
|
|
|
+ check_ok = ''
|
|
|
+
|
|
|
if request.method == 'POST':
|
|
|
check_data = 'acl (' + name + ')'
|
|
|
else:
|
|
|
check_data = None
|
|
|
|
|
|
- test = re.search('^사용자:(.+)$', name)
|
|
|
- if test:
|
|
|
- test = test.groups()
|
|
|
- ip = ip_check()
|
|
|
-
|
|
|
+ user_data = re.search('^사용자:(.+)$', name)
|
|
|
+ if user_data:
|
|
|
if custom(conn)[2] == 0:
|
|
|
return redirect('/login')
|
|
|
- elif test[0] != ip:
|
|
|
+ elif user_data.groups()[0] != ip_check():
|
|
|
if admin_check(conn, 5, check_data) != 1:
|
|
|
- return re_error(conn, '/error/3')
|
|
|
+ if check_data:
|
|
|
+ return re_error(conn, '/error/3')
|
|
|
+ else:
|
|
|
+ check_ok = 'disable'
|
|
|
else:
|
|
|
if admin_check(conn, 5, check_data) != 1:
|
|
|
- return re_error(conn, '/error/3')
|
|
|
+ if check_data:
|
|
|
+ return re_error(conn, '/error/3')
|
|
|
+ else:
|
|
|
+ check_ok = 'disabled'
|
|
|
|
|
|
if request.method == 'POST':
|
|
|
- if request.form['select'] == 'admin':
|
|
|
- sql = 'admin'
|
|
|
- elif request.form['select'] == 'all':
|
|
|
- sql = 'all'
|
|
|
- elif request.form['select'] == 'user':
|
|
|
- sql = 'user'
|
|
|
+ curs.execute("select title from acl where title = ?", [name])
|
|
|
+ if curs.fetchall():
|
|
|
+ curs.execute("update acl set dec = ? where title = ?", [request.form.get('dec', ''), name])
|
|
|
+ curs.execute("update acl set dis = ? where title = ?", [request.form.get('dis', ''), name])
|
|
|
+ curs.execute("update acl set why = ? where title = ?", [request.form.get('why', ''), name])
|
|
|
else:
|
|
|
- sql = ''
|
|
|
-
|
|
|
- if sql == '':
|
|
|
+ curs.execute("insert into acl (title, dec, dis, why) values (?, ?, ?, ?)", [name, request.form.get('dec', ''), request.form.get('dis', ''), request.form.get('why', '')])
|
|
|
+
|
|
|
+ curs.execute("select title from acl where title = ? and dec = '' and dis = ''", [name])
|
|
|
+ if curs.fetchall():
|
|
|
curs.execute("delete from acl where title = ?", [name])
|
|
|
- else:
|
|
|
- curs.execute("select title from acl where title = ?", [name])
|
|
|
- if curs.fetchall():
|
|
|
- curs.execute("update acl set dec = ? where title = ?", [sql, name])
|
|
|
- else:
|
|
|
- curs.execute("insert into acl (title, dec, dis, why) values (?, ?, '', '')", [name, sql])
|
|
|
|
|
|
conn.commit()
|
|
|
|
|
|
- return redirect('/w/' + url_pas(name))
|
|
|
+ return redirect('/acl/' + url_pas(name))
|
|
|
else:
|
|
|
- acl_list = ['', '', '']
|
|
|
+ data = '<h2>문서 ACL</h2><select name="dec" ' + check_ok + '>'
|
|
|
+
|
|
|
+ if re.search('^사용자:', name):
|
|
|
+ acl_list = [['', '일반'], ['user', '가입자'], ['all', '모두']]
|
|
|
+ else:
|
|
|
+ acl_list = [['', '일반'], ['user', '가입자'], ['admin', '관리자']]
|
|
|
|
|
|
curs.execute("select dec from acl where title = ?", [name])
|
|
|
- acl_d = curs.fetchall()
|
|
|
- if acl_d:
|
|
|
- if test and acl_d[0][0] == 'all':
|
|
|
- now = '모두'
|
|
|
-
|
|
|
- acl_list[0] = 'selected="selected"'
|
|
|
- elif not test and acl_d[0][0] == 'admin':
|
|
|
- now = lang_data['admin']
|
|
|
-
|
|
|
- acl_list[0] = 'selected="selected"'
|
|
|
- elif acl_d[0][0] == 'user':
|
|
|
- now = '가입자'
|
|
|
-
|
|
|
- acl_list[1] = 'selected="selected"'
|
|
|
+ acl_data = curs.fetchall()
|
|
|
+ for data_list in acl_list:
|
|
|
+ if acl_data and acl_data[0][0] == data_list[0]:
|
|
|
+ check = 'selected="selected"'
|
|
|
else:
|
|
|
- now = '일반'
|
|
|
-
|
|
|
- acl_list[2] = 'selected="selected"'
|
|
|
- else:
|
|
|
- now = '일반'
|
|
|
+ check = ''
|
|
|
|
|
|
- acl_list[2] = 'selected="selected"'
|
|
|
-
|
|
|
- if test:
|
|
|
- plus = '<option value="all" ' + acl_list[0] + '>모두</option>'
|
|
|
- else:
|
|
|
- plus = '<option value="admin" ' + acl_list[0] + '>' + lang_data['admin'] + '</option>'
|
|
|
+ data += '<option value="' + data_list[0] + '" ' + check + '>' + data_list[1] + '</option>'
|
|
|
+
|
|
|
+ data += '</select>'
|
|
|
+
|
|
|
+ if not re.search('^사용자:', name):
|
|
|
+ data += '<br><br><h2>토론 ACL</h2><select name="dis" ' + check_ok + '>'
|
|
|
+
|
|
|
+ curs.execute("select dis, why from acl where title = ?", [name])
|
|
|
+ acl_data = curs.fetchall()
|
|
|
+ for data_list in acl_list:
|
|
|
+ if acl_data and acl_data[0][0] == data_list[0]:
|
|
|
+ check = 'selected="selected"'
|
|
|
+ else:
|
|
|
+ check = ''
|
|
|
+
|
|
|
+ data += '<option value="' + data_list[0] + '" ' + check + '>' + data_list[1] + '</option>'
|
|
|
+
|
|
|
+ data += '</select>'
|
|
|
+
|
|
|
+ if acl_data:
|
|
|
+ data += '<hr><input value="' + html.escape(acl_data[0][1]) + '" placeholder="사유" name="why" type="text" ' + check_ok + '>'
|
|
|
|
|
|
return html_minify(render_template(skin_check(conn),
|
|
|
imp = [name, wiki_set(conn, 1), custom(conn), other2([' (ACL)', 0])],
|
|
|
- data = '<form method="post"><span>현재 ACL : ' + now + '</span><hr><select name="select">' + plus + '<option value="user" ' + acl_list[1] + '>가입자</option><option value="normal" ' + acl_list[2] + '>일반</option></select><hr><button type="submit">ACL 변경</button></form>',
|
|
|
+ data = '<form method="post">' + data + '<hr><button type="submit">ACL 변경</button></form>',
|
|
|
menu = [['w/' + url_pas(name), lang_data['document']], ['manager', lang_data['admin']]]
|
|
|
))
|
|
|
|
|
|
@@ -2645,7 +2642,7 @@ def user_admin(name = None):
|
|
|
div += '<option value="' + data[0] + '">' + data[0] + '</option>'
|
|
|
|
|
|
return html_minify(render_template(skin_check(conn),
|
|
|
- imp = [name, wiki_set(conn, 1), custom(conn), other2([' (권한 부여)', 0])],
|
|
|
+ imp = [name, wiki_set(conn, 1), custom(conn), other2([' (권한 관리)', 0])],
|
|
|
data = '<form method="post"><select name="select">' + div + '</select><hr><button type="submit">변경</button></form>',
|
|
|
menu = [['manager', lang_data['admin']]]
|
|
|
))
|
|
|
@@ -2781,23 +2778,6 @@ def read_view(name = None):
|
|
|
response_data = 404
|
|
|
else_data = ''
|
|
|
|
|
|
- if not num:
|
|
|
- curs.execute("select dec from acl where title = ?", [name])
|
|
|
- acl_d = curs.fetchall()
|
|
|
- if acl_d:
|
|
|
- if acl_d[0][0] == 'admin':
|
|
|
- acl = ' (' + lang_data['admin'] + ')'
|
|
|
- elif acl_d[0][0] == 'user':
|
|
|
- acl = ' (가입자)'
|
|
|
- else:
|
|
|
- curs.execute('select data from other where name = "edit"')
|
|
|
- set_data = curs.fetchall()
|
|
|
- if set_data:
|
|
|
- if set_data[0][0] == 'admin':
|
|
|
- acl = ' (' + lang_data['admin'] + ')'
|
|
|
- elif set_data[0][0] == 'user':
|
|
|
- acl = ' (가입자)'
|
|
|
-
|
|
|
m = re.search("^사용자:([^/]*)", name)
|
|
|
if m:
|
|
|
g = m.groups()
|
|
|
@@ -2813,13 +2793,10 @@ def read_view(name = None):
|
|
|
else:
|
|
|
acl = ''
|
|
|
|
|
|
- curs.execute("select dec from acl where title = ?", [name])
|
|
|
- data = curs.fetchall()
|
|
|
- if data:
|
|
|
- if data[0][0] == 'all':
|
|
|
- acl += ' (모두)'
|
|
|
- elif data[0][0] == 'user':
|
|
|
- acl += ' (가입자)'
|
|
|
+ curs.execute("select dec from acl where title = ?", [name])
|
|
|
+ data = curs.fetchall()
|
|
|
+ if data:
|
|
|
+ acl += ' (ACL O)'
|
|
|
|
|
|
if request.args.get('froms', None):
|
|
|
else_data = re.sub('\r\n#(?:redirect|넘겨주기) (?P<in>(?:(?!\r\n).)+)\r\n', ' * [[\g<in>]] 문서로 넘겨주기', '\r\n' + else_data + '\r\n')
|
|
|
@@ -2827,33 +2804,30 @@ def read_view(name = None):
|
|
|
else_data = re.sub('\r\n$', '', else_data)
|
|
|
|
|
|
end_data = namumark(conn, name, else_data, 0)
|
|
|
-
|
|
|
- if data_none == 1:
|
|
|
- menu = [['edit/' + url_pas(name), '생성']]
|
|
|
- else:
|
|
|
- menu = [['edit/' + url_pas(name), '수정']]
|
|
|
-
|
|
|
- menu += [['topic/' + url_pas(name), '토론'], ['history/' + url_pas(name), lang_data['history']], ['xref/' + url_pas(name), '역링크']]
|
|
|
-
|
|
|
- if admin_memu == 1:
|
|
|
- menu += [['acl/' + url_pas(name), 'ACL']]
|
|
|
-
|
|
|
- if request.args.get('froms', None):
|
|
|
- menu += [['w/' + url_pas(name), '넘기기']]
|
|
|
- end_data = '<ul id="redirect"><li><a href="/w/' + url_pas(request.args.get('froms', None)) + '?froms=' + url_pas(name) + '">' + request.args.get('froms', None) + '</a>에서 넘어 왔습니다.</li></ul><br>' + end_data
|
|
|
-
|
|
|
- if uppage != 0:
|
|
|
- menu += [['w/' + url_pas(uppage), '상위']]
|
|
|
-
|
|
|
- if down:
|
|
|
- menu += [['down/' + url_pas(name), '하위']]
|
|
|
-
|
|
|
+
|
|
|
if num:
|
|
|
menu = [['history/' + url_pas(name), lang_data['history']]]
|
|
|
sub = ' (' + str(num) + lang_data['version'] + ')'
|
|
|
acl = ''
|
|
|
r_date = 0
|
|
|
else:
|
|
|
+ if data_none == 1:
|
|
|
+ menu = [['edit/' + url_pas(name), '생성']]
|
|
|
+ else:
|
|
|
+ menu = [['edit/' + url_pas(name), '수정']]
|
|
|
+
|
|
|
+ menu += [['topic/' + url_pas(name), '토론'], ['history/' + url_pas(name), lang_data['history']], ['xref/' + url_pas(name), '역링크'], ['acl/' + url_pas(name), 'ACL']]
|
|
|
+
|
|
|
+ if request.args.get('froms', None):
|
|
|
+ menu += [['w/' + url_pas(name), '넘기기']]
|
|
|
+ end_data = '<ul id="redirect"><li><a href="/w/' + url_pas(request.args.get('froms', None)) + '?froms=' + url_pas(name) + '">' + request.args.get('froms', None) + '</a>에서 넘어 왔습니다.</li></ul><br>' + end_data
|
|
|
+
|
|
|
+ if uppage != 0:
|
|
|
+ menu += [['w/' + url_pas(uppage), '상위']]
|
|
|
+
|
|
|
+ if down:
|
|
|
+ menu += [['down/' + url_pas(name), '하위']]
|
|
|
+
|
|
|
curs.execute("select date from history where title = ? order by date desc limit 1", [name])
|
|
|
date = curs.fetchall()
|
|
|
if date:
|