diff --git a/app.py b/app.py index bb5ca01..3c07244 100644 --- a/app.py +++ b/app.py @@ -99,8 +99,15 @@ def render_markdown(text): def index(): conn = func.connectToDb() cursor = conn.cursor(dictionary=True) - cursor.execute("SELECT * FROM questions WHERE answered=%s ORDER BY creation_date DESC", (True,)) - questions = cursor.fetchall() + + cursor.execute("SELECT * FROM questions WHERE answered=%s AND pinned=%s ORDER BY creation_date DESC", (True, True)) + pinned_questions = cursor.fetchall() + + cursor.execute("SELECT * FROM questions WHERE answered=%s AND pinned=%s ORDER BY creation_date DESC", (True, False)) + non_pinned_questions = cursor.fetchall() + + questions = pinned_questions + non_pinned_questions + cursor.execute("SELECT * FROM answers ORDER BY creation_date DESC") answers = cursor.fetchall() @@ -123,7 +130,7 @@ def index(): def inbox(): conn = func.connectToDb() cursor = conn.cursor(dictionary=True) - cursor.execute("SELECT * FROM questions WHERE answered=%s", (False,)) + cursor.execute("SELECT * FROM questions WHERE answered=%s ORDER BY creation_date DESC", (False,)) questions = cursor.fetchall() cursor.close() @@ -146,13 +153,13 @@ def login(): admin_password = request.form.get('admin_password') if admin_password == os.environ.get('ADMIN_PASSWORD'): session['logged_in'] = True - return redirect(url_for('admin.index')) + return redirect(url_for('index')) else: flash("Wrong password", 'danger') return redirect(url_for('admin.login')) else: if logged_in: - return redirect('admin.index') + return redirect('index') else: return render_template('admin/login.html') @@ -276,6 +283,34 @@ def returnToInbox(): return {'message': 'Successfully returned question to inbox.'}, 200 +@api_bp.route('/pin_question/', methods=['POST']) +def pinQuestion(): + question_id = request.args.get('question_id', '') + if not question_id: + abort(400, "Missing 'question_id' attribute or 'question_id' is empty") + + conn = func.connectToDb() + cursor = conn.cursor() + cursor.execute("UPDATE questions SET pinned=%s WHERE id=%s", (True, question_id)) + cursor.close() + conn.close() + + return {'message': 'Successfully pinned question.'}, 200 + +@api_bp.route('/unpin_question/', methods=['POST']) +def unpinQuestion(): + question_id = request.args.get('question_id', '') + if not question_id: + abort(400, "Missing 'question_id' attribute or 'question_id' is empty") + + conn = func.connectToDb() + cursor = conn.cursor() + cursor.execute("UPDATE questions SET pinned=%s WHERE id=%s", (False, question_id)) + cursor.close() + conn.close() + + return {'message': 'Successfully unpinned question.'}, 200 + @api_bp.route('/add_answer/', methods=['POST']) @loginRequired def addAnswer(): diff --git a/constants.py b/constants.py index 8d7dff1..25e254f 100644 --- a/constants.py +++ b/constants.py @@ -2,6 +2,6 @@ antiSpamFile = 'wordlist.txt' blacklistFile = 'word_blacklist.txt' configFile = 'config.json' appName = 'CatAsk' -version = '1.1.0' +version = '1.2.0' # id (identifier) is to be interpreted as described in https://semver.org/#spec-item-9 version_id = '-alpha' diff --git a/functions.py b/functions.py index fa0282a..539ffb7 100644 --- a/functions.py +++ b/functions.py @@ -110,9 +110,12 @@ def renderMarkdown(text): 'i', 'br', 's', - 'del' + 'del', + 'a' ] - allowed_attrs = {} + allowed_attrs = { + 'a': 'href' + } # hard_wrap=True means that newlines will be # converted into
tags # @@ -141,8 +144,8 @@ def generateMetadata(question=None, answer=None): # if question is specified, generate metadata for that question if question and answer: metadata.update({ - 'title': trimContent(f"{question['content']}", 150) + " | " + cfg['instance']['title'], - 'description': trimContent(f"{answer['content']}", 150), + 'title': trimContent(question['content'], 150) + " | " + cfg['instance']['title'], + 'description': trimContent(answer['content'], 150), 'url': cfg['instance']['fullBaseUrl'] + url_for('viewQuestion', question_id=question['id']), 'image': cfg['instance']['image'] }) diff --git a/schema.sql b/schema.sql index 220ce44..e11851c 100644 --- a/schema.sql +++ b/schema.sql @@ -11,7 +11,8 @@ CREATE TABLE IF NOT EXISTS questions ( creation_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, content TEXT NOT NULL, answered BOOLEAN NOT NULL DEFAULT FALSE, - answer_id INT + answer_id INT, + pinned BOOLEAN NOT NULL DEFAULT FALSE ) ENGINE=InnoDB; ALTER TABLE questions diff --git a/static/css/style.css b/static/css/style.css index 4d2533a..45ae569 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -25,7 +25,7 @@ --bs-primary-rgb: 127,98,240; --bs-primary-subtle: color-mix(in srgb, var(--bs-primary) 10%, transparent); --bs-danger-bg-subtle: #2c0b0e; - --bs-link-color: color-mix(in srgb, var(--bs-primary) 70%, white); + --bs-link-color: color-mix(in srgb, var(--bs-primary) 60%, white); } .btn { diff --git a/static/icons/catask-1024.png b/static/icons/catask-1024.png index ca0e738..ca9d91b 100644 Binary files a/static/icons/catask-1024.png and b/static/icons/catask-1024.png differ diff --git a/static/icons/catask-128.png b/static/icons/catask-128.png index e2919a5..73f103f 100644 Binary files a/static/icons/catask-128.png and b/static/icons/catask-128.png differ diff --git a/static/icons/catask-16.png b/static/icons/catask-16.png index 1bee573..1659563 100644 Binary files a/static/icons/catask-16.png and b/static/icons/catask-16.png differ diff --git a/static/icons/catask-256.png b/static/icons/catask-256.png index 43f0bb0..b4c0969 100644 Binary files a/static/icons/catask-256.png and b/static/icons/catask-256.png differ diff --git a/static/icons/catask-32.png b/static/icons/catask-32.png index f8c1bb8..8b9e6b6 100644 Binary files a/static/icons/catask-32.png and b/static/icons/catask-32.png differ diff --git a/static/icons/catask-512.png b/static/icons/catask-512.png index 2044ecd..7eebad3 100644 Binary files a/static/icons/catask-512.png and b/static/icons/catask-512.png differ diff --git a/static/icons/catask-64.png b/static/icons/catask-64.png index 0a1a100..f82f337 100644 Binary files a/static/icons/catask-64.png and b/static/icons/catask-64.png differ diff --git a/static/icons/catask.svg b/static/icons/catask.svg index e06373b..1a7b701 100644 --- a/static/icons/catask.svg +++ b/static/icons/catask.svg @@ -8,6 +8,9 @@ id="svg9" sodipodi:docname="catask.svg" inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)" + inkscape:export-filename="catask-16.png" + inkscape:export-xdpi="4.0421052" + inkscape:export-ydpi="4.0421052" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" @@ -22,8 +25,8 @@ inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" inkscape:zoom="0.48026316" - inkscape:cx="346.68493" - inkscape:cy="368.54794" + inkscape:cx="316.49315" + inkscape:cy="533.04109" inkscape:window-width="1280" inkscape:window-height="962" inkscape:window-x="0" @@ -42,7 +45,7 @@ d="M372.86 340.836C361.458 364.034 337.594 380 310 380H69.9999C42.4057 380 18.5407 364.033 7.13956 340.835C0.0540733 303.832 2.43122 265.23 21.5492 228.243C22.3857 226.625 24.2343 225.797 25.9907 226.281C61.5026 236.067 97.5669 264.887 131.247 340.717C131.983 342.374 133.752 343.367 135.546 343.105C160.424 339.463 177.96 339.001 190 339.001C202.04 339.001 219.576 339.463 244.454 343.105C246.248 343.367 248.017 342.375 248.753 340.717C282.433 264.887 318.497 236.067 354.009 226.281C355.765 225.797 357.614 226.625 358.45 228.243C377.568 265.23 379.945 303.833 372.86 340.836Z" fill="#DA75FF" id="path1" - style="fill:#e59bff;fill-opacity:1" /> + style="fill:#caacff;fill-opacity:1" /> @@ -77,12 +80,14 @@ gradientUnits="userSpaceOnUse"> + id="stop4" + offset="0" + style="stop-color:#9a69ce;stop-opacity:1;" /> + style="stop-color:#6f42c1;stop-opacity:1;" /> + style="stop-color:#caacff;stop-opacity:1;" /> -
+
+
+ + + +
+ +
+
+ + -
{{ question.content | render_markdown }}
- -
-
-
- -
- - + -
- -{% endfor %} {% else %}

Inbox is currently empty.

{% endif %} diff --git a/templates/index.html b/templates/index.html index 9dccc62..2a7904d 100644 --- a/templates/index.html +++ b/templates/index.html @@ -40,6 +40,10 @@
{% if combined %}
+ {% if cfg.showQuestionCount == true %} +

{{ len(combined) }} question(s)

+ {% endif %} +
{% for item in combined %}
@@ -51,28 +55,39 @@ {{ cfg.anonName }} {% endif %} -
{{ formatRelativeTime(str(item.question.creation_date)) }}
+
{{ formatRelativeTime(str(item.question.creation_date)) }}
{{ item.question.content | render_markdown }}
- + {% for answer in item.answers %} +
{{ answer.content | render_markdown }}
+
{% endfor %} diff --git a/templates/view_question.html b/templates/view_question.html index c2478dc..05f662d 100644 --- a/templates/view_question.html +++ b/templates/view_question.html @@ -2,7 +2,7 @@ {% block title %}"{{ trimContent(question.content, 15) }}" - "{{ trimContent(answer.content, 15) }}"{% endblock %} {% block content %}
-
+
@@ -12,7 +12,7 @@
{{ question.content | render_markdown }}
-

{{ answer.content }}

+
{{ answer.content | render_markdown }}