From 208b9ba67490a2894da10775d0d7d3683bb7360d Mon Sep 17 00:00:00 2001
From: Xevion
Date: Mon, 28 Mar 2022 18:17:52 -0500
Subject: [PATCH 01/33] Fix new post form length filters not redirecting
correctly
I never ran into this error myself, but I think if we did, it's possible it could have caused some kind of infinite loop. Or it would just error because the form is mapped to POST requests only - unless the browser would take the redirect and send the POST request there - but that wouldn't make sense given it's normal purpose.
---
forms.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/forms.py b/forms.py
index b53c493..b41156e 100644
--- a/forms.py
+++ b/forms.py
@@ -31,10 +31,10 @@ def new_post():
if len(post_text) < 15:
flash('Must have at least 15 characters of text.')
- return redirect(url_for('forms.new_post'))
+ return redirect(url_for('main.feed'))
elif len(post_text) > 1000:
flash('Cannot have more than 1000 characters of text.')
- return redirect(url_for('forms.new_post'))
+ return redirect(url_for('main.feed'))
post = Post(author=current_user.id, text=post_text)
db.session.add(post)
From 2973f3ca817df7a412586f36922fbbe9d73d07e9 Mon Sep 17 00:00:00 2001
From: Xevion
Date: Mon, 28 Mar 2022 18:29:02 -0500
Subject: [PATCH 02/33] Add profanity filter to comment and post creation
- For Heroku, I worry someone might add a racial slur or something. This isn't perfect, but it's good enough.
---
Pipfile | 1 +
Pipfile.lock | 370 ++++++++++++++++++++++++++++++++++++++++++++++++++-
forms.py | 13 ++
3 files changed, 383 insertions(+), 1 deletion(-)
diff --git a/Pipfile b/Pipfile
index a3bba7c..67baa41 100644
--- a/Pipfile
+++ b/Pipfile
@@ -14,6 +14,7 @@ faker = "*"
humanize = "*"
gunicorn = "*"
psycopg2 = "*"
+profanity-filter = "*"
[dev-packages]
diff --git a/Pipfile.lock b/Pipfile.lock
index ff4a891..3015107 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "e51737b38157b4d1e2b57226a5a07dbce7558baeef5895e41cede4d2809de74b"
+ "sha256": "e847c58401b17c65209f6f6d77aacb1926b2f8805007f4629b67fcf95dccb209"
},
"pipfile-spec": 6,
"requires": {
@@ -16,6 +16,57 @@
]
},
"default": {
+ "blis": {
+ "hashes": [
+ "sha256:148f59a0a47a38ce82e3afc50c709494d5e5a494bef28ce1519c7a17346c645b",
+ "sha256:1667db8439d9ca41c0c1f0ea954d87462be01b125436c4b264f73603c9fb4e82",
+ "sha256:3e024f103522e72a27019cfcfe14569522a394f5d651565560a18040fdd69a6c",
+ "sha256:4a48eeaa506f176bcac306378f5e8063697c93e26d2418fcbe053e8912019090",
+ "sha256:5d4a81f9438db7a19ac8e64ad41331f65a659ea8f3bb1889a9c2088cfd9fe104",
+ "sha256:64bef63b1abd5b41819ea53897bdbc03c631a59c1757a9393e6ae0828692f31c",
+ "sha256:680480dfa16b354f2e4d584edb8d36f0505ed8df12939beee2d161aea7bb3609",
+ "sha256:76d13dbcd648ca33dfc83569bb219d0696e4f6e5ad00b9f538332a3bdb28ff30",
+ "sha256:7865e39cac4e10506afc49213938fb7e13bf73ca980c9c20ffad2de4ef858f43",
+ "sha256:a0183760604b14e8eb671a431d06606594def03c36aaaa2a2e7b7f88382dac76",
+ "sha256:b5e0acc760daf5c3b45bce44653943e3a04d81c21c5b92213ed51664525dc24e",
+ "sha256:bead485e5d79d3eb62a8df55618743878fb3cba606aaf926153db5803270b185",
+ "sha256:cfb7d730fef706f3ea4389196ce5f610f24cc83f828c498a275c12f05f0cf5c4",
+ "sha256:d6055ced65d6581ab4f1da0d3f6ec14c60512474c5c9b3210c9f30dd7dd1447d",
+ "sha256:e22145110864bcffb1d52cb57050b67b8a8ecd43c7c0a1ac0bcdb2c85c8bf416",
+ "sha256:f4109cce38e644e81d923836b34024905d59e88c8fb48b89b420f4d7661cd89f"
+ ],
+ "version": "==0.7.7"
+ },
+ "cached-property": {
+ "hashes": [
+ "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130",
+ "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"
+ ],
+ "version": "==1.5.2"
+ },
+ "catalogue": {
+ "hashes": [
+ "sha256:584d78e7f4c3c6e2fd498eb56dfc8ef1f4ff738480237de2ccd26cbe2cf47172",
+ "sha256:d74d1d856c6b36a37bf14aa6dbbc27d0582667b7ab979a6108e61a575e8723f5"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==1.0.0"
+ },
+ "certifi": {
+ "hashes": [
+ "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872",
+ "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"
+ ],
+ "version": "==2021.10.8"
+ },
+ "charset-normalizer": {
+ "hashes": [
+ "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597",
+ "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"
+ ],
+ "markers": "python_version >= '3'",
+ "version": "==2.0.12"
+ },
"click": {
"hashes": [
"sha256:19a4baa64da924c5e0cd889aba8e947f280309f1a2ce0947a3e3a7bcb7cc72d6",
@@ -32,6 +83,27 @@
"markers": "platform_system == 'Windows'",
"version": "==0.4.4"
},
+ "cymem": {
+ "hashes": [
+ "sha256:04676d696596b0db3f3c5a3936bab12fb6f24278921a6622bb185e61765b2b4d",
+ "sha256:169725b5816959d34de2545b33fee6a8021a6e08818794a426c5a4f981f17e5e",
+ "sha256:2aa3fa467d906cd2c27fa0a2e2952dd7925f5fcc7973fab6d815ef6acb25aad8",
+ "sha256:4749f220e4c06ec44eb10de13794ff0508cdc4f8eff656cf49cab2cdb3122c0c",
+ "sha256:492084aef23ac2ff3da3729e9d36340bc91a96c2dc8c3a82a1926e384ab52412",
+ "sha256:4f87fe087f2ae36c3e20e2b1a29d7f76a28c035372d0a97655f26223d975235a",
+ "sha256:6b0d1a6b0a1296f31fa9e4b7ae5ea49394084ecc883b1ae6fec4844403c43468",
+ "sha256:700540b68e96a7056d0691d467df2bbaaf0934a3e6fe2383669998cbee19580a",
+ "sha256:971cf0a8437dfb4185c3049c086e463612fe849efadc0f5cc153fc81c501da7d",
+ "sha256:a93fba62fe79dbf6fc4d5b6d804a6e114b44af3ff3d40a28833ee39f21bd336b",
+ "sha256:af3c01e6b20f9e6c07c7d7cdb7f710e49889d3906c9a3e039546ee6636a34b9a",
+ "sha256:b8e1c18bb00800425576710468299153caad20c64ddb6819d40a6a34e21ee21c",
+ "sha256:c59293b232b53ebb47427f16cf648e937022f489cff36c11d1d8a1f0075b6609",
+ "sha256:d7a59cef8f2fa25d12e2c30138f8623acbd43ad2715e730a709e49c5eef8e1b0",
+ "sha256:dd52d8a81881804625df88453611175ab7e0099b34f52204da1f6940cf2e83c9",
+ "sha256:ea535f74ab6024e7416f93de564e5c81fb7c0964b96280de66f60aeb05f0cf53"
+ ],
+ "version": "==2.0.6"
+ },
"faker": {
"hashes": [
"sha256:188961065fb5c78ea639f42176f55100f72c90c3a3179ac6c955c4bd712b0511",
@@ -141,6 +213,14 @@
"index": "pypi",
"version": "==4.0.0"
},
+ "idna": {
+ "hashes": [
+ "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
+ "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
+ ],
+ "markers": "python_version >= '3'",
+ "version": "==3.3"
+ },
"importlib-metadata": {
"hashes": [
"sha256:1208431ca90a8cca1a6b8af391bb53c1a2db74e5d1cef6ddced95d4b2062edc6",
@@ -211,6 +291,120 @@
"markers": "python_version >= '3.7'",
"version": "==2.1.1"
},
+ "more-itertools": {
+ "hashes": [
+ "sha256:43e6dd9942dffd72661a2c4ef383ad7da1e6a3e968a927ad7a6083ab410a688b",
+ "sha256:7dc6ad46f05f545f900dd59e8dfb4e84a4827b97b3cfecb175ea0c7d247f6064"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==8.12.0"
+ },
+ "murmurhash": {
+ "hashes": [
+ "sha256:00a5252b569d3f914b5bd0bce72d2efe9c0fb91a9703556ea1b608b141c68f2d",
+ "sha256:1431d817e1fff1ed35f8dc54dd5b4d70165ec98076de8aca351805f8037293f3",
+ "sha256:2911bc3e8040dfaac536b141539b0351915f1439953f0aa9e957f082cff035a6",
+ "sha256:5c7b8cc4a8db1c821b80f8ca70a25c3166b14d68ecef8693a117c6a0b1d74ace",
+ "sha256:773411eba268bf524c012e781f4405aacb9ef4edc063d1f6b38bbf06358b988e",
+ "sha256:7dc5a79346afa07f14384926c335c0c455226d687d1305b9378264875b450e51",
+ "sha256:8de08d145c85bb7ba89cb1b591742e3ef54cede73e35f62752af687a4a1859f7",
+ "sha256:90a8e06872015d6f9f66a42669e003a1df8be229defef69cd98546f4cb25546d",
+ "sha256:92cd7196974307143ce8e9e9b6e22e0a57abf30bdd5a1effe696b4825677e616",
+ "sha256:9d69cc0ffc0ef6d37399b8a0484a44f9877e531ebc164e55105e89738ed52089",
+ "sha256:a78d53f047c3410ce4c589d9b47090f628f844ed5694418144e63cfe7f3da7e9",
+ "sha256:ab326b172dc470331490bda516d4d6d7578c91445ad83a2a3418ac1b9c5f9f55",
+ "sha256:cdd1036688341413e5adef32b3fd58e8b44f24405f394f90129f39ed879e4f24",
+ "sha256:de267459d040c96727ba141075d5bc983ec69c6f75b6df1b703e3b5cd7090382",
+ "sha256:e40790fdaf65213d70da4ed9229f16f6d6376310dc8fc23eacc98e6151c6ae7e",
+ "sha256:f4ef3b26229ff192032a12653d637313e1231d23e788b83a2f4a3d8e2bf2d031"
+ ],
+ "version": "==1.0.6"
+ },
+ "numpy": {
+ "hashes": [
+ "sha256:07a8c89a04997625236c5ecb7afe35a02af3896c8aa01890a849913a2309c676",
+ "sha256:08d9b008d0156c70dc392bb3ab3abb6e7a711383c3247b410b39962263576cd4",
+ "sha256:201b4d0552831f7250a08d3b38de0d989d6f6e4658b709a02a73c524ccc6ffce",
+ "sha256:2c10a93606e0b4b95c9b04b77dc349b398fdfbda382d2a39ba5a822f669a0123",
+ "sha256:3ca688e1b9b95d80250bca34b11a05e389b1420d00e87a0d12dc45f131f704a1",
+ "sha256:48a3aecd3b997bf452a2dedb11f4e79bc5bfd21a1d4cc760e703c31d57c84b3e",
+ "sha256:568dfd16224abddafb1cbcce2ff14f522abe037268514dd7e42c6776a1c3f8e5",
+ "sha256:5bfb1bb598e8229c2d5d48db1860bcf4311337864ea3efdbe1171fb0c5da515d",
+ "sha256:639b54cdf6aa4f82fe37ebf70401bbb74b8508fddcf4797f9fe59615b8c5813a",
+ "sha256:8251ed96f38b47b4295b1ae51631de7ffa8260b5b087808ef09a39a9d66c97ab",
+ "sha256:92bfa69cfbdf7dfc3040978ad09a48091143cffb778ec3b03fa170c494118d75",
+ "sha256:97098b95aa4e418529099c26558eeb8486e66bd1e53a6b606d684d0c3616b168",
+ "sha256:a3bae1a2ed00e90b3ba5f7bd0a7c7999b55d609e0c54ceb2b076a25e345fa9f4",
+ "sha256:c34ea7e9d13a70bf2ab64a2532fe149a9aced424cd05a2c4ba662fd989e3e45f",
+ "sha256:dbc7601a3b7472d559dc7b933b18b4b66f9aa7452c120e87dfb33d02008c8a18",
+ "sha256:e7927a589df200c5e23c57970bafbd0cd322459aa7b1ff73b7c2e84d6e3eae62",
+ "sha256:f8c1f39caad2c896bc0018f699882b345b2a63708008be29b1f355ebf6f933fe",
+ "sha256:f950f8845b480cffe522913d35567e29dd381b0dc7e4ce6a4a9f9156417d2430",
+ "sha256:fade0d4f4d292b6f39951b6836d7a3c7ef5b2347f3c420cd9820a1d90d794802",
+ "sha256:fdf3c08bce27132395d3c3ba1503cac12e17282358cb4bddc25cc46b0aca07aa"
+ ],
+ "markers": "python_version >= '3.8'",
+ "version": "==1.22.3"
+ },
+ "ordered-set": {
+ "hashes": [
+ "sha256:a7bfa858748c73b096e43db14eb23e2bc714a503f990c89fac8fab9b0ee79724"
+ ],
+ "markers": "python_version >= '2.7'",
+ "version": "==3.1.1"
+ },
+ "ordered-set-stubs": {
+ "hashes": [
+ "sha256:29274ae21ca2cfc127874042461996e748bb3c68ae7618ffd0028309554e2dff",
+ "sha256:4f76c7470a4e364feb51fe42c4bcd968c8ff9dab520aff9287b394e14da1d1ee"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==0.1.3"
+ },
+ "plac": {
+ "hashes": [
+ "sha256:398cb947c60c4c25e275e1f1dadf027e7096858fb260b8ece3b33bcff90d985f",
+ "sha256:487e553017d419f35add346c4c09707e52fa53f7e7181ce1098ca27620e9ceee"
+ ],
+ "version": "==1.1.3"
+ },
+ "poetry-version": {
+ "hashes": [
+ "sha256:ba259257640cd36c76375563a001b9e85c6f54d764cc56b3eed0f3b5cefb0317",
+ "sha256:f7d77b5c4f0761c5a5b9031f199c79c7f81d125676248053ea84e69ad79faa89"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==0.1.5"
+ },
+ "preshed": {
+ "hashes": [
+ "sha256:3af09f4cfcdaca085fd87dac8107617c4e2bb0ad1458f953841b71e9728287f5",
+ "sha256:58661bea8d0d63a648588511407285e43d43627e27f836e30819801fb3c75d70",
+ "sha256:5f99837e7353ce1fa81f0074d4b15f36e0af5af60a2a54d4d11e13cb09768a9e",
+ "sha256:61b2ea656cb1c38d544cc774f1c2ad1cdab23167b46b35310a7e211d4ba9c6d0",
+ "sha256:66a71ced487516cf81fd0431a3a843514262ae2f33e9a7688b87562258fa75d5",
+ "sha256:6c98f725d8478f3ade4ab1ea00f50a92d2d9406d37276bc46fd8bab1d47452c4",
+ "sha256:87e1add41b7f6236a3ccc34788f47ab8682bc28e8a2d369089062e274494c1a0",
+ "sha256:8c60a400babfc5b25ba371fda7041be227f7c625e1fb7a43329c2c08fe00a53b",
+ "sha256:92a8f49d17a63537a8beed48a049b62ef168ca07e0042a5b2bcdf178a1fb5d48",
+ "sha256:a279c138ad1d5be02547b1545254929588414b01571fe637016367f6a1aa11de",
+ "sha256:cfe1495fcfc7f479de840ddc4f426dbb55351e218ae5c8712c1269183a4d0060",
+ "sha256:e03ae3eee961106a517fcd827b5a7c51f7317236b3e665c989054ab8dc381d28",
+ "sha256:ea8aa9610837e907e8442e79300df0a861bfdb4dcaf026a5d9642a688ad04815",
+ "sha256:eaffbc71fdb8625f9aac4fe7e19e20bf318d1421ea05903bebe3e6ffef27b587",
+ "sha256:f92e752a868ea2690e1b38c4b775251a145e0fce36b9bdd972539e8271b7a23a",
+ "sha256:fb3b7588a3a0f2f2f1bf3fe403361b2b031212b73a37025aea1df7215af3772a"
+ ],
+ "version": "==3.0.6"
+ },
+ "profanity-filter": {
+ "hashes": [
+ "sha256:67f9baffb13cb1256dd1b717c56caea2f058ec3bfd8e40024beb7f0fff298f94",
+ "sha256:8f5c908ac7f14c8e3bf4f039da21dbddbc14ed2061191450b0670ad3bdc50d0b"
+ ],
+ "index": "pypi",
+ "version": "==1.3.3"
+ },
"psycopg2": {
"hashes": [
"sha256:06f32425949bd5fe8f625c49f17ebb9784e1e4fe928b7cce72edc36fb68e4c0c",
@@ -228,6 +422,47 @@
"index": "pypi",
"version": "==2.9.3"
},
+ "pydantic": {
+ "hashes": [
+ "sha256:085ca1de245782e9b46cefcf99deecc67d418737a1fd3f6a4f511344b613a5b3",
+ "sha256:086254884d10d3ba16da0588604ffdc5aab3f7f09557b998373e885c690dd398",
+ "sha256:0b6037175234850ffd094ca77bf60fb54b08b5b22bc85865331dd3bda7a02fa1",
+ "sha256:0fe476769acaa7fcddd17cadd172b156b53546ec3614a4d880e5d29ea5fbce65",
+ "sha256:1d5278bd9f0eee04a44c712982343103bba63507480bfd2fc2790fa70cd64cf4",
+ "sha256:2cc6a4cb8a118ffec2ca5fcb47afbacb4f16d0ab8b7350ddea5e8ef7bcc53a16",
+ "sha256:2ee7e3209db1e468341ef41fe263eb655f67f5c5a76c924044314e139a1103a2",
+ "sha256:3011b975c973819883842c5ab925a4e4298dffccf7782c55ec3580ed17dc464c",
+ "sha256:3c3b035103bd4e2e4a28da9da7ef2fa47b00ee4a9cf4f1a735214c1bcd05e0f6",
+ "sha256:4c68c3bc88dbda2a6805e9a142ce84782d3930f8fdd9655430d8576315ad97ce",
+ "sha256:574936363cd4b9eed8acdd6b80d0143162f2eb654d96cb3a8ee91d3e64bf4cf9",
+ "sha256:5a79330f8571faf71bf93667d3ee054609816f10a259a109a0738dac983b23c3",
+ "sha256:5e48ef4a8b8c066c4a31409d91d7ca372a774d0212da2787c0d32f8045b1e034",
+ "sha256:6c5b77947b9e85a54848343928b597b4f74fc364b70926b3c4441ff52620640c",
+ "sha256:742645059757a56ecd886faf4ed2441b9c0cd406079c2b4bee51bcc3fbcd510a",
+ "sha256:7bdfdadb5994b44bd5579cfa7c9b0e1b0e540c952d56f627eb227851cda9db77",
+ "sha256:815ddebb2792efd4bba5488bc8fde09c29e8ca3227d27cf1c6990fc830fd292b",
+ "sha256:8b5ac0f1c83d31b324e57a273da59197c83d1bb18171e512908fe5dc7278a1d6",
+ "sha256:96f240bce182ca7fe045c76bcebfa0b0534a1bf402ed05914a6f1dadff91877f",
+ "sha256:a733965f1a2b4090a5238d40d983dcd78f3ecea221c7af1497b845a9709c1721",
+ "sha256:ab624700dc145aa809e6f3ec93fb8e7d0f99d9023b713f6a953637429b437d37",
+ "sha256:b2571db88c636d862b35090ccf92bf24004393f85c8870a37f42d9f23d13e032",
+ "sha256:bbbc94d0c94dd80b3340fc4f04fd4d701f4b038ebad72c39693c794fd3bc2d9d",
+ "sha256:c0727bda6e38144d464daec31dff936a82917f431d9c39c39c60a26567eae3ed",
+ "sha256:c556695b699f648c58373b542534308922c46a1cda06ea47bc9ca45ef5b39ae6",
+ "sha256:c86229333cabaaa8c51cf971496f10318c4734cf7b641f08af0a6fbf17ca3054",
+ "sha256:c8d7da6f1c1049eefb718d43d99ad73100c958a5367d30b9321b092771e96c25",
+ "sha256:c8e9dcf1ac499679aceedac7e7ca6d8641f0193c591a2d090282aaf8e9445a46",
+ "sha256:cb23bcc093697cdea2708baae4f9ba0e972960a835af22560f6ae4e7e47d33f5",
+ "sha256:d1e4c28f30e767fd07f2ddc6f74f41f034d1dd6bc526cd59e63a82fe8bb9ef4c",
+ "sha256:d9c9bdb3af48e242838f9f6e6127de9be7063aad17b32215ccc36a09c5cf1070",
+ "sha256:dee5ef83a76ac31ab0c78c10bd7d5437bfdb6358c95b91f1ba7ff7b76f9996a1",
+ "sha256:e0896200b6a40197405af18828da49f067c2fa1f821491bc8f5bde241ef3f7d7",
+ "sha256:f5a64b64ddf4c99fe201ac2724daada8595ada0d102ab96d019c1555c2d6441d",
+ "sha256:f947352c3434e8b937e3aa8f96f47bdfe6d92779e44bb3f41e4c213ba6a32145"
+ ],
+ "markers": "python_full_version >= '3.6.1'",
+ "version": "==1.9.0"
+ },
"python-dateutil": {
"hashes": [
"sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86",
@@ -252,6 +487,45 @@
"index": "pypi",
"version": "==2022.1"
},
+ "redis": {
+ "hashes": [
+ "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2",
+ "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==3.5.3"
+ },
+ "requests": {
+ "hashes": [
+ "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61",
+ "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
+ "version": "==2.27.1"
+ },
+ "ruamel.yaml": {
+ "hashes": [
+ "sha256:08aaaa74ff66565024ecabf9ba2db212712382a21c0458f9a91c623a1fa83b34",
+ "sha256:23f2efb872d2ebe3d5428b4f1a8f30cbf59f56e780c4981c155411ee65572673",
+ "sha256:38718e69270141c403b5fc539f774ed394568f8a5195b507991f5b690356facb",
+ "sha256:44da2be1153e173f90ad8775d4ac4237a3c06cfbb9660c1c1980271621833faa",
+ "sha256:4b1674a936cdae9735578d4fd64bcbc6cfbb77a1a8f7037a50c6e3874ba4c9d8",
+ "sha256:51d49c870aca850e652e2cd1c9bea9b52b77d13ad52b0556de496c1d264ea65f",
+ "sha256:63dc8c6147a4cf77efadf2ae0f34e89e03de79289298bb941b7ae333d5d4020b",
+ "sha256:6672798c6b52a976a7b24e20665055852388c83198d88029d3c76e2197ac221a",
+ "sha256:6b6025f9b6a557e15e9fdfda4d9af0b57cd8d59ff98e23a0097ab2d7c0540f07",
+ "sha256:7b750252e3d1ec5b53d03be508796c04a907060900c7d207280b7456650ebbfc",
+ "sha256:847177699994f9c31adf78d1ef1ff8f069ef0241e744a3ee8b30fbdaa914cc1e",
+ "sha256:8e42f3067a59e819935a2926e247170ed93c8f0b2ab64526f888e026854db2e4",
+ "sha256:922d9e483c05d9000256640026f277fcc0c2e1e9271d05acada8e6cfb4c8b721",
+ "sha256:92a8ca79f9173cca29ca9663b49d9c936aefc4c8a76f39318b0218c8f3626438",
+ "sha256:ab8eeca4de4decf0d0a42cb6949d354da9fc70a2d9201f0dd55186c599b2e3a5",
+ "sha256:bd4b60b649f4a81086f70cd56eff4722018ef36a28094c396f1a53bf450bd579",
+ "sha256:fc6471ef15b69e454cca82433ac5f84929d9f3e2d72b9e54b06850b6b7133cc0",
+ "sha256:ffc89770339191acbe5a15041950b5ad9daec7d659619b0ed9dad8c9c80c26f3"
+ ],
+ "version": "==0.15.100"
+ },
"setuptools": {
"hashes": [
"sha256:8f4813dd6a4d6cc17bde85fb2e635fe19763f96efbb0ddf5575562e5ee0bc47a",
@@ -268,6 +542,25 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.16.0"
},
+ "spacy": {
+ "hashes": [
+ "sha256:0416e708c0b672e79400aeade070d2ec91052ca9942cc378ee9f833a3ae9b6ed",
+ "sha256:2d454e4d08c8263ab0bb4e63445e9565c5924f588dcf26bcb0add3a4d98a6042",
+ "sha256:34570a7571d8bf08c696003423589b750ac684c0b79f3e3672e3d941a44f2452",
+ "sha256:4942670ee16e153ddb6d5ae85eb03c39c33a2ef19b9ea0423d28f63536e21d72",
+ "sha256:65ecac35b9812f146d99d91610d38f9b8d849a8164fccf0fdcfd4cf7e2826618",
+ "sha256:6ebc5a7f7da70a793cc1d2569097bdf80fbce66d7e39e17a157f95c258bec78d",
+ "sha256:96eaa20d6074158b5686740a40ba78aac4f52759f8acec8af9db4d586bc49f1f",
+ "sha256:abddbf424233a842aec91119bc8c4578890c587809d33f353f334a5383d8f490",
+ "sha256:b4f1a02d62e861a044b8fbe8a0ce89e49d5a63c3a5bfc5849cb1d4f0247b8ab9",
+ "sha256:c0f2315fea23497662e28212f89af3a03667f97c867c597b599c37ab84092e22",
+ "sha256:c774d5cd869c0d086fd092eeacbb72296c8d470dda73bc45ed9f9f9ca5822759",
+ "sha256:c831bcec3f3c0b425f753248763c838b57f31edfca2dfcafce205f6f2b548d7e",
+ "sha256:fc59c5cf8f363d7c1dfd779bae7eafe99dc98a74063267f9f8df4154b65f7a2f"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==2.3.7"
+ },
"sqlalchemy": {
"hashes": [
"sha256:04164e0063feb7aedd9d073db0fd496edb244be40d46ea1f0d8990815e4b8c34",
@@ -309,6 +602,81 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==1.4.32"
},
+ "srsly": {
+ "hashes": [
+ "sha256:11447f8e659e1f62f29302252fb057f179031457b36c83426027182f624fe565",
+ "sha256:23c7205b8c1cac49a03521bee37f0afe3680d9f0ec18c75ab3ac39bd3e15272b",
+ "sha256:2615b8713dfe793ca57925076b0869385d56754816b1eaee5490a6827a1cb5c7",
+ "sha256:334f29435099e644a8047b63d60b8386a98b5f7b4739f7efc86b46ca0200aa0e",
+ "sha256:4c43a1f28e555891a1e65650adea2c5d0f0fe4b3d63821de65c8357f32c3a11c",
+ "sha256:779ebfaa3cf1d5c0f1286ac1baf06af5f2a17bb103622992c71acc6ac20b2781",
+ "sha256:8fc4c0641537262e15c7b5b57edc47487b15ac47b696adcb81e0a770ef78e8f5",
+ "sha256:a1449da4195e30a3bd1fd3122e5b1a0c57703843c590643555c412fc87132aa0",
+ "sha256:a2746afccfd4f51f0793cccc2b6d5e8a564c962870feec5c77408244c1dbb3c5",
+ "sha256:a696e9c925e91f76ec53840c55483a4fbf76cb717424410a4f249d4805439038",
+ "sha256:b5b887328ac6e210842560fcf32a29c2a9c1ed38c6d47479cadc03d81940da8c",
+ "sha256:d3dd796372367c71946d0cd6f734e49db3d99dd13a57bdac937d9eb62689fc9e",
+ "sha256:fd5e1e01f5fd0f532a6f3977bb74facc42f1b7155402ee3d06c07a73e83e3c47"
+ ],
+ "version": "==1.0.5"
+ },
+ "thinc": {
+ "hashes": [
+ "sha256:02b71ae5a0fa906a0aca968bd65589e0ab9fabd511e57be839774228b1509224",
+ "sha256:10bafe5ddce698180098345b9c55f762dc3456558be844d35d64175e511581b6",
+ "sha256:24086aa0fb72f466782115d529574a825c89afa62eb817962b9339f61ab50e0d",
+ "sha256:29a47ad0289dda0520b5af8538b30e8134553130200b83c34311feb71739968d",
+ "sha256:309ec4cae81f4de2e4e4fbd0bcb52b10bef4b1a6352c6a9143f6a53d3b1060ef",
+ "sha256:5743fde41706252ec6ce4737c68d3505f7e1cc3d4431174a17149838d594f8cb",
+ "sha256:5774007b5c52501cab5e2970cadca84923b4c420fff06172f2d0c86531973ce8",
+ "sha256:8b647de79fe5f98cd327983bf0e27d006b48ad9694ceabdb9a3832b614ed1618",
+ "sha256:c408ab24b24e6368ce4b6ddebb579118042a22d3f2f2c4e19ca67e3eadc9ed33",
+ "sha256:c43ed753aa70bc619e42e168be4926c8a47799af6121ff0727ba99b330afbb44",
+ "sha256:cce68c5ea54cd32cef661858363509afdedad047027e8cdf0dc4edec0c2cc010",
+ "sha256:d01ab1480d37ebefcac22d63ffe01916c9f025ae3dbdbe5824ac3ea5cce8e3fd",
+ "sha256:fae320de65af70786c1526ffc33b88f2da650d3106f5f9a06b37f0ac3944a44f"
+ ],
+ "version": "==7.4.5"
+ },
+ "tomlkit": {
+ "hashes": [
+ "sha256:4e1bd6c9197d984528f9ff0cc9db667c317d8881288db50db20eeeb0f6b0380b",
+ "sha256:f044eda25647882e5ef22b43a1688fb6ab12af2fc50e8456cdfc751c873101cf"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==0.5.11"
+ },
+ "tqdm": {
+ "hashes": [
+ "sha256:4230a49119a416c88cc47d0d2d32d5d90f1a282d5e497d49801950704e49863d",
+ "sha256:6461b009d6792008d0000e1b0c7ca50195ec78c0e808a3a6b668a56a3236c3a5"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==4.63.1"
+ },
+ "typing-extensions": {
+ "hashes": [
+ "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42",
+ "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==4.1.1"
+ },
+ "urllib3": {
+ "hashes": [
+ "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14",
+ "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
+ "version": "==1.26.9"
+ },
+ "wasabi": {
+ "hashes": [
+ "sha256:152245d892030a3a7b511038e9472acff6d0e237cfe4123fef0d147f2d3274fc",
+ "sha256:f40f317981d019903db5b69eb2bf78519c9e165c1dfdbd0452e4ca81ff9a31d2"
+ ],
+ "version": "==0.9.0"
+ },
"werkzeug": {
"hashes": [
"sha256:1421ebfc7648a39a5c58c601b154165d05cf47a3cd0ccb70857cbdacf6c8f2b8",
diff --git a/forms.py b/forms.py
index b41156e..564199b 100644
--- a/forms.py
+++ b/forms.py
@@ -1,10 +1,12 @@
from flask import Blueprint, flash, redirect, request, url_for
from flask_login import current_user, login_required
+from profanity_filter import ProfanityFilter
from database import db
from models import User, Post, Comment
blueprint = Blueprint('forms', __name__)
+pf = ProfanityFilter()
@blueprint.route('/user//edit', methods=['POST'])
@@ -36,6 +38,10 @@ def new_post():
flash('Cannot have more than 1000 characters of text.')
return redirect(url_for('main.feed'))
+ if not pf.is_clean(post_text):
+ flash('Sorry, profanity is not allowed on runnerspace.')
+ return redirect(url_for('main.feed'))
+
post = Post(author=current_user.id, text=post_text)
db.session.add(post)
db.session.commit()
@@ -53,6 +59,13 @@ def add_comment(post_id: int):
if len(comment_text) > 50:
flash('Cannot have more than 50 characters of text.')
return redirect(url_for('main.view_post', post_id=post_id))
+ elif len(comment_text) < 5:
+ flash('Your comment must have at least 5 characters of text.')
+ return redirect(url_for('main.view_post', post_id=post_id))
+
+ if not pf.is_clean(comment_text):
+ flash('Sorry, profanity is not allowed on runnerspace.')
+ return redirect(url_for('main.view_post', post_id=post_id))
comment = Comment(post=post.id, author=current_user.id, text=comment_text)
db.session.add(comment)
From 040e505561eba2542badbac7a43d4d5d2b183f73 Mon Sep 17 00:00:00 2001
From: Xevion
Date: Mon, 28 Mar 2022 18:54:17 -0500
Subject: [PATCH 03/33] Improve/add error messages to look better visually
- Have not tested everywhere, but should work okayish
---
static/styles.css | 15 +++++++--
static/styles.css.map | 2 +-
static/styles.scss | 18 ++++++++--
templates/pages/auth/login.html | 56 +++++++++++++++----------------
templates/pages/auth/signup.html | 57 ++++++++++++++++----------------
templates/pages/feed.html | 16 ++++-----
templates/pages/post.html | 8 +++++
templates/pages/user_edit.html | 4 +--
8 files changed, 105 insertions(+), 71 deletions(-)
diff --git a/static/styles.css b/static/styles.css
index f7a5d3b..2f63e2b 100644
--- a/static/styles.css
+++ b/static/styles.css
@@ -124,12 +124,12 @@ nav .links li:not(:last-child)::after, footer .links li:not(:last-child)::after
}
form {
- margin: 0 auto;
+ margin: 1em auto;
width: fit-content;
}
form.login-form {
border: 1px solid darkblue;
- padding: 10px;
+ padding: 0.7em;
}
form .field {
padding: 4px;
@@ -262,6 +262,7 @@ form button {
}
.post-box .post-author {
margin-top: 1em;
+ margin-bottom: 0.5em;
font-size: 0.8em;
border-bottom: 1px solid grey;
padding-bottom: 0.8em;
@@ -312,4 +313,14 @@ form button {
margin: 1em;
}
+span.error-message {
+ margin: 0.5em;
+ border: 1px solid red;
+ padding: 0.3em;
+}
+span.error-message.center-message {
+ margin: 0 auto;
+ display: table;
+}
+
/*# sourceMappingURL=styles.css.map */
diff --git a/static/styles.css.map b/static/styles.css.map
index 9bcc18c..d32f6c1 100644
--- a/static/styles.css.map
+++ b/static/styles.css.map
@@ -1 +1 @@
-{"version":3,"sourceRoot":"","sources":["styles.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EAOE;;AALA;EAEE;;;AASJ;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;;AAKJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AACA;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;;AAKN;EACE;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAIA;EACE;EACA;;AAEA;EACE;;AAEA;EACE;;;AAMR;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAIA;EACE;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;;;AAMF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;;AAKF;EACE;EACA;EACA;EACA;;;AAMR;EACE;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;EACA;;AAIJ;EAEE;EACA;EACA;;;AAMR;EACE;;AACA;EACE;EACA;EACA;EACA;;AAEA;EACE;;;AAKN;EACE;EACA","file":"styles.css"}
\ No newline at end of file
+{"version":3,"sourceRoot":"","sources":["styles.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EAOE;;AALA;EAEE;;;AASJ;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;;AAKJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;;AAKN;EACE;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAIA;EACE;EACA;;AAEA;EACE;;AAEA;EACE;;;AAMR;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAIA;EACE;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;;;AAMF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;;AAKF;EACE;EACA;EACA;EACA;;;AAMR;EACE;EACA;;AAEA;EACE;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;EACA;;AAIJ;EAEE;EACA;EACA;;;AAMR;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;;AAKN;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA","file":"styles.css"}
\ No newline at end of file
diff --git a/static/styles.scss b/static/styles.scss
index 5190556..be2ad22 100644
--- a/static/styles.scss
+++ b/static/styles.scss
@@ -59,6 +59,7 @@ body, html {
#img-logo {
height: 100%;
box-sizing: border-box;
+
img {
max-width: 100%;
max-height: 80px;
@@ -145,12 +146,12 @@ nav, footer {
}
form {
- margin: 0 auto;
+ margin: 1em auto;
width: fit-content;
&.login-form {
border: 1px solid darkblue;
- padding: 10px
+ padding: 0.7em;
}
.field {
@@ -308,6 +309,7 @@ form {
.post-author {
margin-top: 1em;
+ margin-bottom: 0.5em;
font-size: 0.8em;
border-bottom: 1px solid grey;
padding-bottom: 0.8em;
@@ -350,6 +352,7 @@ form {
.new-users, .statistics {
margin-top: 0 !important;
+
> div {
border: 1px solid grey;
padding: 0.2em;
@@ -366,3 +369,14 @@ form {
min-height: 235px;
margin: 1em;
}
+
+span.error-message {
+ margin: 0.5em;
+ border: 1px solid red;
+ padding: 0.3em;
+
+ &.center-message {
+ margin: 0 auto;
+ display: table;
+ }
+}
diff --git a/templates/pages/auth/login.html b/templates/pages/auth/login.html
index 2f1e799..dadc103 100644
--- a/templates/pages/auth/login.html
+++ b/templates/pages/auth/login.html
@@ -1,35 +1,35 @@
{% extends 'layouts/index.html' %}
{% block content %}
- {% with messages = get_flashed_messages() %}
- {% if messages %}
-
+ {% with messages = get_flashed_messages() %}
+ {% if messages %}
+
{{ messages[0] }}
-
- {% endif %}
- {% endwith %}
-
©2022 Runnerspace.live All Rights Reserved.
diff --git a/templates/layouts/header.html b/templates/layouts/header.html
index 1698940..3679961 100644
--- a/templates/layouts/header.html
+++ b/templates/layouts/header.html
@@ -24,7 +24,7 @@
{#
My Messages #}
{# Blog #}
{# Groups #}
- About
+ About
From 2a443979b9ca40c0ecbfd6d8dce233f569178b37 Mon Sep 17 00:00:00 2001
From: Xevion
Date: Tue, 29 Mar 2022 23:40:02 -0500
Subject: [PATCH 25/33] Hide "New Users" box when no users are in database
- Also improved CSS organization
- Use space-around flex to center stats box
- Capitalized 'runnerspace'
---
static/styles.css | 13 ++++++++-----
static/styles.css.map | 2 +-
static/styles.scss | 30 +++++++++++++++++-------------
templates/layouts/index.html | 28 +++++++++++++++-------------
4 files changed, 41 insertions(+), 32 deletions(-)
diff --git a/static/styles.css b/static/styles.css
index 88e0b58..3a50c1c 100644
--- a/static/styles.css
+++ b/static/styles.css
@@ -309,20 +309,23 @@ nav .links li:not(:last-child)::after, footer .links li:not(:last-child)::after
text-decoration: none;
}
-.new-users, .statistics {
+.index-flex {
+ display: flex;
+ justify-content: space-around;
+}
+.index-flex .new-users, .index-flex .statistics {
margin-top: 0 !important;
}
-.new-users > div, .statistics > div {
+.index-flex .new-users > div, .index-flex .statistics > div {
border: 1px solid grey;
padding: 0.2em;
width: fit-content;
padding-right: 1.5em;
}
-.new-users > div a, .statistics > div a {
+.index-flex .new-users > div a, .index-flex .statistics > div a {
text-decoration: none;
}
-
-.statistics > div {
+.index-flex .statistics > div {
min-height: 235px;
margin: 1em;
}
diff --git a/static/styles.css.map b/static/styles.css.map
index a673361..abf669c 100644
--- a/static/styles.css.map
+++ b/static/styles.css.map
@@ -1 +1 @@
-{"version":3,"sourceRoot":"","sources":["styles.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EAOE;;AALA;EAEE;;;AASJ;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;;AAKJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;;AAKN;EACE;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAIA;EACE;EACA;;AAEA;EACE;;AAEA;EACE;;;AAMR;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAIA;EACE;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;;;AAMF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;;AAKF;EACE;EACA;EACA;EACA;;;AAMR;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAKN;EACE;EACA;;AAIJ;EAEE;EACA;EACA;;;AAMR;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;;AAKN;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EAME;EACA;EACA;EAEA;EACA;EACA;;AAXA;EACE;;;AAcN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;;;AAGF;EACE","file":"styles.css"}
\ No newline at end of file
+{"version":3,"sourceRoot":"","sources":["styles.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EAOE;;AALA;EAEE;;;AASJ;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;;AAKJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;;AAKN;EACE;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAIA;EACE;EACA;;AAEA;EACE;;AAEA;EACE;;;AAMR;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAIA;EACE;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;;;AAMF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;;AAKF;EACE;EACA;EACA;EACA;;;AAMR;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAKN;EACE;EACA;;AAIJ;EAEE;EACA;EACA;;;AAMR;EACE;EACA;;AACA;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAKN;EACE;EACA;;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EAME;EACA;EACA;EAEA;EACA;EACA;;AAXA;EACE;;;AAcN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;;;AAGF;EACE","file":"styles.css"}
\ No newline at end of file
diff --git a/static/styles.scss b/static/styles.scss
index 8f2ef40..4e307fb 100644
--- a/static/styles.scss
+++ b/static/styles.scss
@@ -367,24 +367,28 @@ nav, footer {
}
}
-.new-users, .statistics {
- margin-top: 0 !important;
+.index-flex {
+ display: flex;
+ justify-content: space-around;
+ .new-users, .statistics {
+ margin-top: 0 !important;
- > div {
- border: 1px solid grey;
- padding: 0.2em;
- width: fit-content;
- padding-right: 1.5em;
+ > div {
+ border: 1px solid grey;
+ padding: 0.2em;
+ width: fit-content;
+ padding-right: 1.5em;
- a {
- text-decoration: none;
+ a {
+ text-decoration: none;
+ }
}
}
-}
-.statistics > div {
- min-height: 235px;
- margin: 1em;
+ .statistics > div {
+ min-height: 235px;
+ margin: 1em;
+ }
}
span.error-message {
diff --git a/templates/layouts/index.html b/templates/layouts/index.html
index 57c1bc2..88d3602 100644
--- a/templates/layouts/index.html
+++ b/templates/layouts/index.html
@@ -1,21 +1,23 @@
{% extends 'layouts/base.html' %}
{% block content %}
-
-
-
New Users
-
-
- {% for new_user in new_users %}
- {{ new_user.username }} as
- of {{ new_user.get_registration_delta() }} ago
-
- {% endfor %}
-
+
+ {% if new_users|length > 0 %}
+
+
New Users
+
+
+ {% for new_user in new_users %}
+ {{ new_user.username }} as
+ of {{ new_user.get_registration_delta() }} ago
+
+ {% endfor %}
+
+
-
+ {% endif %}
-
runnerspace Statistics
+
Runnerspace Statistics
{% with comments = stats['total_comments'], posts = stats['total_posts'], users = stats['total_users'] %}
From bfb69621e152e3dd4139f3e7ad579976c72868fe Mon Sep 17 00:00:00 2001
From: Xevion
Date: Wed, 30 Mar 2022 00:21:55 -0500
Subject: [PATCH 26/33] Add like querying/checking/rendering to Feed posts
- Change runnerspace.live to Runnerspace
- Change post viewing URL to say /post/:id: instead of /feed/:id:
---
models.py | 28 ++++++++++++++++++++++++++++
routes.py | 16 +---------------
static/styles.css | 10 ++++++++++
static/styles.css.map | 2 +-
static/styles.scss | 11 +++++++++++
templates/layouts/footer.html | 2 +-
templates/pages/feed.html | 4 +++-
7 files changed, 55 insertions(+), 18 deletions(-)
diff --git a/models.py b/models.py
index bdc7b9d..472da85 100644
--- a/models.py
+++ b/models.py
@@ -3,6 +3,7 @@ import json
from typing import List
import humanize
+from flask import url_for
from flask_login import UserMixin
from sqlalchemy import func
@@ -27,6 +28,9 @@ class User(UserMixin, db.Model):
posts_liked = db.relationship("PostLike", backref=db.backref('user', lazy='joined'), lazy='dynamic', cascade='all, delete-orphan')
comments_liked = db.relationship("CommentLike", backref=db.backref('user', lazy='joined'), lazy='dynamic', cascade='all, delete-orphan')
+ def get_url(self) -> str:
+ return url_for('main.view_user', username=self.username)
+
def get_last_seen_text(self) -> str:
delta: datetime.timedelta = datetime.datetime.utcnow() - self.last_seen
if delta > MAXIMUM_ONLINE_DELTA:
@@ -70,6 +74,14 @@ class User(UserMixin, db.Model):
def display_about(self) -> str:
return self.about_me or "This user hasn't written a bio yet."
+ def has_liked_post(self, post_id: int) -> bool:
+ """Check whether a user has liked a given post."""
+ return db.session.query(PostLike.id).filter_by(post_id=post_id, user_id=self.id).first() is not None
+
+ def has_liked_comment(self, comment_id: int) -> bool:
+ """Check whether a user has liked a given post."""
+ return db.session.query(CommentLike.id).filter_by(comment_id=comment_id, user_id=user_id).first() is not None
+
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
@@ -84,6 +96,22 @@ class Post(db.Model):
delta: datetime.timedelta = datetime.datetime.utcnow() - self.date_posted
return humanize.naturaldelta(delta)
+ def get_like_count(self) -> bool:
+ return PostLike.query.filter_by(post_id=self.id).count()
+
+ def get_like_text(self) -> str:
+ like_count = self.get_like_count()
+ top_likes = PostLike.query.filter_by(post_id=self.id).order_by(PostLike.timestamp.asc()).limit(3)
+ users = [like.user for like in top_likes]
+ names = [f'{user.name} ' for user in users]
+
+ if like_count >= 3: format_string = '{0}, {1} and {2} has liked this post.'
+ elif like_count == 2: format_string = '{0} and {1} has liked this post.'
+ elif like_count == 1: format_string = '{0} has liked this post.'
+ else: format_string = '0 likes'
+
+ return format_string.format(*names)
+
class PostLike(db.Model):
id = db.Column(db.Integer, primary_key=True)
diff --git a/routes.py b/routes.py
index b66af4d..67f71fb 100644
--- a/routes.py
+++ b/routes.py
@@ -40,17 +40,12 @@ def feed():
return render_template('pages/feed.html', posts=posts, form=form)
-@blueprint.route('/feed/')
+@blueprint.route('/post/')
def view_post(post_id: int):
post = Post.query.get_or_404(post_id)
return render_template('pages/post.html', form=NewCommentForm(), post=post)
-# @blueprint.route('/messages')
-# def messages():
-# return render_template('pages/messages.html')
-
-
@blueprint.route('/search')
def search():
return render_template('pages/search.html')
@@ -81,12 +76,3 @@ def edit_user(username: str):
form.process(obj=user)
return render_template('pages/user_edit.html', form=form)
-
-# @blueprint.route('/blogs')
-# def blogs():
-# return render_template('pages/blogs.html')
-#
-#
-# @blueprint.route('/groups')
-# def groups():
-# return render_template('pages/groups.html')
diff --git a/static/styles.css b/static/styles.css
index 3a50c1c..e141901 100644
--- a/static/styles.css
+++ b/static/styles.css
@@ -250,6 +250,16 @@ nav .links li:not(:last-child)::after, footer .links li:not(:last-child)::after
padding: 1.5em;
padding-bottom: 0.8em;
margin: 0.45em;
+ position: relative;
+}
+.post-box .fa-heart {
+ position: absolute;
+ top: 1em;
+ right: 1em;
+ color: #b0c9f3;
+}
+.post-box .fa-heart.liked {
+ color: #1b53a8;
}
.post-box .post-author {
margin-top: 1em;
diff --git a/static/styles.css.map b/static/styles.css.map
index abf669c..0731870 100644
--- a/static/styles.css.map
+++ b/static/styles.css.map
@@ -1 +1 @@
-{"version":3,"sourceRoot":"","sources":["styles.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EAOE;;AALA;EAEE;;;AASJ;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;;AAKJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;;AAKN;EACE;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAIA;EACE;EACA;;AAEA;EACE;;AAEA;EACE;;;AAMR;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAIA;EACE;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;;;AAMF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;;AAKF;EACE;EACA;EACA;EACA;;;AAMR;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAKN;EACE;EACA;;AAIJ;EAEE;EACA;EACA;;;AAMR;EACE;EACA;;AACA;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAKN;EACE;EACA;;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EAME;EACA;EACA;EAEA;EACA;EACA;;AAXA;EACE;;;AAcN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;;;AAGF;EACE","file":"styles.css"}
\ No newline at end of file
+{"version":3,"sourceRoot":"","sources":["styles.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EAOE;;AALA;EAEE;;;AASJ;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;;AAKJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;;AAKN;EACE;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAIA;EACE;EACA;;AAEA;EACE;;AAEA;EACE;;;AAMR;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAIA;EACE;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;;;AAMF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;;AAKF;EACE;EACA;EACA;EACA;;;AAMR;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AACA;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAKN;EACE;EACA;;AAIJ;EAEE;EACA;EACA;;;AAMR;EACE;EACA;;AACA;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAKN;EACE;EACA;;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EAME;EACA;EACA;EAEA;EACA;EACA;;AAXA;EACE;;;AAcN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;;;AAGF;EACE","file":"styles.css"}
\ No newline at end of file
diff --git a/static/styles.scss b/static/styles.scss
index 4e307fb..314d0b2 100644
--- a/static/styles.scss
+++ b/static/styles.scss
@@ -296,6 +296,17 @@ nav, footer {
padding: 1.5em;
padding-bottom: 0.8em;
margin: 0.45em;
+ position: relative;
+
+ .fa-heart {
+ position: absolute;
+ top: 1em;
+ right: 1em;
+ color: #b0c9f3;
+ &.liked {
+ color: #1b53a8;
+ }
+ }
.post-author {
margin-top: 1em;
diff --git a/templates/layouts/footer.html b/templates/layouts/footer.html
index e7b3bf9..1da8218 100644
--- a/templates/layouts/footer.html
+++ b/templates/layouts/footer.html
@@ -9,7 +9,7 @@
About
- ©2022 Runnerspace.live All Rights Reserved.
+ ©2022 Runnerspace All Rights Reserved.
| {{ now().isoformat(sep=' ', timespec='milliseconds') }} UTC
diff --git a/templates/pages/feed.html b/templates/pages/feed.html
index e350b4b..d37bf04 100644
--- a/templates/pages/feed.html
+++ b/templates/pages/feed.html
@@ -10,16 +10,18 @@
{% endif %}
-
+
{% for post in posts %}
+
{{ post.text }}
{% with comment_count = post.comments|length %}
Posted by {{ post.author.name }}
{{ post.get_time_ago() }} ago . |
{{ comment_count }} comment{{ comment_count|pluralize }}
+ | {{ post.get_like_text()|safe }}
{% endwith %}
From c5c3b01dfa3e84c969c284b15ba31f5b488df1b0 Mon Sep 17 00:00:00 2001
From: Xevion
Date: Wed, 30 Mar 2022 01:18:42 -0500
Subject: [PATCH 27/33] Use jQuery to allow users to like/unlike posts with
dynamic updates
- Only the pages that need jQuery and the likes.js script will load it
---
routes.py | 25 +++++++++++++++++++++++--
static/likes.js | 19 +++++++++++++++++++
static/styles.css | 1 +
static/styles.css.map | 2 +-
static/styles.scss | 1 +
templates/layouts/base.html | 19 +++++++++++++++++++
templates/pages/feed.html | 8 +++++---
7 files changed, 69 insertions(+), 6 deletions(-)
create mode 100644 static/likes.js
diff --git a/routes.py b/routes.py
index 67f71fb..0d36540 100644
--- a/routes.py
+++ b/routes.py
@@ -1,7 +1,7 @@
-from flask import Blueprint, redirect, render_template, url_for, request
+from flask import Blueprint, redirect, render_template, url_for, request, jsonify
from flask_login import current_user, login_required
-from models import User, Post, Comment
+from models import User, Post, Comment, PostLike, CommentLike
from forms import NewPostForm, NewCommentForm, EditProfileForm
from database import db
@@ -46,6 +46,27 @@ def view_post(post_id: int):
return render_template('pages/post.html', form=NewCommentForm(), post=post)
+@blueprint.route('/post//like', methods=['POST'])
+@login_required
+def like_post(post_id: int):
+ # Check that the relevant post exists
+ post = db.session.query(Post).get_or_404(post_id)
+
+ # Acquire the relevant PostLike in question
+ post_like = db.session.query(PostLike).filter_by(post=post, user=current_user).first()
+ if post_like is None:
+ post_like = PostLike(post=post, user=current_user)
+ db.session.add(post_like)
+ else:
+ db.session.delete(post_like)
+ post_like = None
+
+ db.session.commit()
+
+ # post_like is only NOT None if the user had not liked it before, but has liked it now after db.commit().
+ return jsonify({'liked': post_like is not None, 'status_text': post.get_like_text()})
+
+
@blueprint.route('/search')
def search():
return render_template('pages/search.html')
diff --git a/static/likes.js b/static/likes.js
new file mode 100644
index 0000000..7fef968
--- /dev/null
+++ b/static/likes.js
@@ -0,0 +1,19 @@
+function like(id) {
+ $.ajax({url: `/post/${id}/like`, method: "POST", dataType: "json"})
+ .done(function (data) {
+ let post_parent = $(`#post-${id}`)
+ let heart_icon = post_parent.find('.fa-heart')
+ let pre_liked = heart_icon.hasClass('liked')
+
+ // Toggle if the current state no longer matches the database state.
+ if (pre_liked !== data.liked) {
+ if (pre_liked)
+ heart_icon.removeClass('liked')
+ else
+ heart_icon.addClass('liked')
+ }
+
+ // Set new state of the like status text
+ post_parent.find('.post-like-status').html(data.status_text)
+ })
+}
diff --git a/static/styles.css b/static/styles.css
index e141901..60d333b 100644
--- a/static/styles.css
+++ b/static/styles.css
@@ -257,6 +257,7 @@ nav .links li:not(:last-child)::after, footer .links li:not(:last-child)::after
top: 1em;
right: 1em;
color: #b0c9f3;
+ cursor: pointer;
}
.post-box .fa-heart.liked {
color: #1b53a8;
diff --git a/static/styles.css.map b/static/styles.css.map
index 0731870..4112a86 100644
--- a/static/styles.css.map
+++ b/static/styles.css.map
@@ -1 +1 @@
-{"version":3,"sourceRoot":"","sources":["styles.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EAOE;;AALA;EAEE;;;AASJ;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;;AAKJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;;AAKN;EACE;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAIA;EACE;EACA;;AAEA;EACE;;AAEA;EACE;;;AAMR;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAIA;EACE;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;;;AAMF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;;AAKF;EACE;EACA;EACA;EACA;;;AAMR;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AACA;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAKN;EACE;EACA;;AAIJ;EAEE;EACA;EACA;;;AAMR;EACE;EACA;;AACA;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAKN;EACE;EACA;;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EAME;EACA;EACA;EAEA;EACA;EACA;;AAXA;EACE;;;AAcN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;;;AAGF;EACE","file":"styles.css"}
\ No newline at end of file
+{"version":3,"sourceRoot":"","sources":["styles.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EAOE;;AALA;EAEE;;;AASJ;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;;AAKJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;;AAKN;EACE;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAIA;EACE;EACA;;AAEA;EACE;;AAEA;EACE;;;AAMR;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAIA;EACE;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;;;AAMF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;;AAKF;EACE;EACA;EACA;EACA;;;AAMR;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AACA;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAKN;EACE;EACA;;AAIJ;EAEE;EACA;EACA;;;AAMR;EACE;EACA;;AACA;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAKN;EACE;EACA;;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EAME;EACA;EACA;EAEA;EACA;EACA;;AAXA;EACE;;;AAcN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;;;AAGF;EACE","file":"styles.css"}
\ No newline at end of file
diff --git a/static/styles.scss b/static/styles.scss
index 314d0b2..27cdbe9 100644
--- a/static/styles.scss
+++ b/static/styles.scss
@@ -303,6 +303,7 @@ nav, footer {
top: 1em;
right: 1em;
color: #b0c9f3;
+ cursor: pointer;
&.liked {
color: #1b53a8;
}
diff --git a/templates/layouts/base.html b/templates/layouts/base.html
index c30633e..b93188b 100644
--- a/templates/layouts/base.html
+++ b/templates/layouts/base.html
@@ -9,6 +9,25 @@
+ {% if use_jquery %}
+
+
+ {% if use_likes %}
+
+ {% endif %}
+ {% endif %}
{% endblock %}
diff --git a/templates/pages/feed.html b/templates/pages/feed.html
index d37bf04..41e9382 100644
--- a/templates/pages/feed.html
+++ b/templates/pages/feed.html
@@ -1,5 +1,7 @@
{% extends 'layouts/index.html' %}
{% from 'macros.html' import render_field %}
+{% set use_jquery = true %}
+{% set use_likes = true %}
{% block content %}
{% if current_user.is_authenticated %}
@@ -13,15 +15,15 @@
{% for post in posts %}
-
-
+
+
{{ post.text }}
{% with comment_count = post.comments|length %}
Posted by {{ post.author.name }}
{{ post.get_time_ago() }} ago . |
{{ comment_count }} comment{{ comment_count|pluralize }}
- | {{ post.get_like_text()|safe }}
+ |
{{ post.get_like_text()|safe }}
{% endwith %}
From fdec448e745fb7495da5daf36bcb6d549ff00564 Mon Sep 17 00:00:00 2001
From: Xevion
Date: Wed, 30 Mar 2022 01:20:01 -0500
Subject: [PATCH 28/33] Show up to 2 usernames, calculate number unshown on
likes status text
- Switched to username for likes display text instead of name
- Reminder: Strict usernames, no spaces, a-Z + 0-9
---
models.py | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/models.py b/models.py
index 472da85..3761ed9 100644
--- a/models.py
+++ b/models.py
@@ -101,18 +101,20 @@ class Post(db.Model):
def get_like_text(self) -> str:
like_count = self.get_like_count()
- top_likes = PostLike.query.filter_by(post_id=self.id).order_by(PostLike.timestamp.asc()).limit(3)
+ top_likes = PostLike.query.filter_by(post_id=self.id).order_by(PostLike.timestamp.asc()).limit(2)
users = [like.user for like in top_likes]
- names = [f'{user.name} ' for user in users]
+ names = [f'{user.username} ' for user in users]
- if like_count >= 3: format_string = '{0}, {1} and {2} has liked this post.'
+ if like_count >= 3: format_string = '{0}, {1} and {other_text} have liked this post.'
elif like_count == 2: format_string = '{0} and {1} has liked this post.'
elif like_count == 1: format_string = '{0} has liked this post.'
else: format_string = '0 likes'
+ others: int = like_count - top_likes.count()
+ if others > 0:
+ return format_string.format(*names, other_text=f'{others} other{"s" if others != 1 else ""}')
return format_string.format(*names)
-
class PostLike(db.Model):
id = db.Column(db.Integer, primary_key=True)
timestamp = db.Column(db.DateTime, server_default=func.now())
From 7da54f656d003039221cf1961def9c4ce0b68742 Mon Sep 17 00:00:00 2001
From: Xevion
Date: Wed, 30 Mar 2022 01:27:28 -0500
Subject: [PATCH 29/33] Limit possible characters in a username heavily to
combat abuse
---
forms.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/forms.py b/forms.py
index d1fb1be..aafe74e 100644
--- a/forms.py
+++ b/forms.py
@@ -5,7 +5,9 @@ from validators import NoProfanity
class RegistrationForm(FlaskForm):
- username = StringField('Username', [validators.Length(min=4, max=25), NoProfanity()])
+ username = StringField('Username', [validators.Length(min=4, max=25),
+ validators.Regexp(r' ^[a-zA-Z0-9]+([._]?[a-zA-Z0-9]+)*$'),
+ NoProfanity()])
name = StringField('Name', [validators.Length(min=2, max=35), NoProfanity()])
password = PasswordField('New Password', [
validators.DataRequired(),
@@ -23,7 +25,7 @@ class LoginForm(FlaskForm):
class EditProfileForm(FlaskForm):
name = RegistrationForm.name
- about_me = TextAreaField('About Me', [validators.Optional(), NoProfanity()], description='Tell us about yourself',)
+ about_me = TextAreaField('About Me', [validators.Optional(), NoProfanity()], description='Tell us about yourself', )
class NewPostForm(FlaskForm):
From d16df75bf57a3eaed88c8e47c8fb7370a86b233f Mon Sep 17 00:00:00 2001
From: Xevion
Date: Wed, 30 Mar 2022 01:33:17 -0500
Subject: [PATCH 30/33] Add better message hints to RegEx validators
I realized that users wouldn't be able to understand why their username
was invalid, so rather than getting rid of it, I added message hints
and split it up into two different validators. If the first one fails,
the second one will show as well.
Not perfect, but better than before by a longshot.
---
forms.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/forms.py b/forms.py
index aafe74e..8a5b0a5 100644
--- a/forms.py
+++ b/forms.py
@@ -6,7 +6,10 @@ from validators import NoProfanity
class RegistrationForm(FlaskForm):
username = StringField('Username', [validators.Length(min=4, max=25),
- validators.Regexp(r' ^[a-zA-Z0-9]+([._]?[a-zA-Z0-9]+)*$'),
+ validators.Regexp(r'^[a-zA-Z0-9_\.]+$',
+ message='Only letters, numbers, underscore character and dots are allowed.'),
+ validators.Regexp(r'^[a-zA-Z0-9]+([._]?[a-zA-Z0-9]+)*$',
+ message='Dots and underscores cannot be at the start of the username, repeat or touch.'),
NoProfanity()])
name = StringField('Name', [validators.Length(min=2, max=35), NoProfanity()])
password = PasswordField('New Password', [
From 11394bfb7e4bcf74fcde6e7827f1d5cc11499600 Mon Sep 17 00:00:00 2001
From: Xevion
Date: Wed, 30 Mar 2022 01:36:32 -0500
Subject: [PATCH 31/33] Re-add red border to form errors, limit like button to
logged in users
---
static/styles.css | 7 +------
static/styles.css.map | 2 +-
static/styles.scss | 11 ++++-------
templates/pages/feed.html | 4 +++-
4 files changed, 9 insertions(+), 15 deletions(-)
diff --git a/static/styles.css b/static/styles.css
index 60d333b..c286caf 100644
--- a/static/styles.css
+++ b/static/styles.css
@@ -341,15 +341,10 @@ nav .links li:not(:last-child)::after, footer .links li:not(:last-child)::after
margin: 1em;
}
-span.error-message {
- margin: 0.5em;
+.errors > li {
border: 1px solid red;
padding: 0.3em;
}
-span.error-message.center-message {
- margin: 0 auto;
- display: table;
-}
form {
background: white;
diff --git a/static/styles.css.map b/static/styles.css.map
index 4112a86..c5aee0f 100644
--- a/static/styles.css.map
+++ b/static/styles.css.map
@@ -1 +1 @@
-{"version":3,"sourceRoot":"","sources":["styles.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EAOE;;AALA;EAEE;;;AASJ;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;;AAKJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;;AAKN;EACE;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAIA;EACE;EACA;;AAEA;EACE;;AAEA;EACE;;;AAMR;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAIA;EACE;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;;;AAMF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;;AAKF;EACE;EACA;EACA;EACA;;;AAMR;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AACA;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAKN;EACE;EACA;;AAIJ;EAEE;EACA;EACA;;;AAMR;EACE;EACA;;AACA;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAKN;EACE;EACA;;;AAIJ;EACE;EACA;EACA;;AAEA;EACE;EACA;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EAME;EACA;EACA;EAEA;EACA;EACA;;AAXA;EACE;;;AAcN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;;;AAGF;EACE","file":"styles.css"}
\ No newline at end of file
+{"version":3,"sourceRoot":"","sources":["styles.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EAOE;;AALA;EAEE;;;AASJ;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;;;AAKJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;EACA;EACA;;AAEA;EACE;EACA;;;AAKN;EACE;EACA;EACA;EACA;;AAEA;EACE;;;AAIJ;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAIA;EACE;EACA;;AAEA;EACE;;AAEA;EACE;;;AAMR;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAIA;EACE;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;;;AAIJ;EACE;;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;;;AAMF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;;AAKF;EACE;EACA;EACA;EACA;;;AAMR;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAIJ;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEA;EACE;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAKN;EACE;EACA;;AAIJ;EAEE;EACA;EACA;;;AAMR;EACE;EACA;;AAEA;EACE;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAKN;EACE;EACA;;;AAIJ;EAEE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EAME;EACA;EACA;EAEA;EACA;EACA;;AAXA;EACE;;;AAcN;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;;;AAGF;EACE","file":"styles.css"}
\ No newline at end of file
diff --git a/static/styles.scss b/static/styles.scss
index 27cdbe9..3f3e4f0 100644
--- a/static/styles.scss
+++ b/static/styles.scss
@@ -304,6 +304,7 @@ nav, footer {
right: 1em;
color: #b0c9f3;
cursor: pointer;
+
&.liked {
color: #1b53a8;
}
@@ -382,6 +383,7 @@ nav, footer {
.index-flex {
display: flex;
justify-content: space-around;
+
.new-users, .statistics {
margin-top: 0 !important;
@@ -403,15 +405,10 @@ nav, footer {
}
}
-span.error-message {
- margin: 0.5em;
+.errors > li {
+ //margin: 0.5em;
border: 1px solid red;
padding: 0.3em;
-
- &.center-message {
- margin: 0 auto;
- display: table;
- }
}
form {
diff --git a/templates/pages/feed.html b/templates/pages/feed.html
index 41e9382..442ddb0 100644
--- a/templates/pages/feed.html
+++ b/templates/pages/feed.html
@@ -16,7 +16,9 @@
{% for post in posts %}
-
+ {% if current_user.is_authenticated %}
+
+ {% endif %}
{{ post.text }}
{% with comment_count = post.comments|length %}
From 765e786231640d5d0ebc4c0141a7e527e2ee3d85 Mon Sep 17 00:00:00 2001
From: Xevion
Date: Wed, 30 Mar 2022 01:41:32 -0500
Subject: [PATCH 32/33] Add like counter to user profile
- Also fixed some issue with get_post_count() method? Weird.
---
models.py | 4 ++--
templates/pages/user.html | 7 +++++--
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/models.py b/models.py
index 3761ed9..8d290e2 100644
--- a/models.py
+++ b/models.py
@@ -53,11 +53,11 @@ class User(UserMixin, db.Model):
def get_post_count(self) -> int:
"""Returns the number of posts this user has made."""
- return Post.query.filter_by(user_id=self.id).count()
+ return Post.query.filter_by(author=self).count()
def get_comment_count(self) -> int:
"""Returns the number of comments this user has made."""
- return Comment.query.filter_by(user_id=self.id).count()
+ return Comment.query.filter_by(author=self).count()
def get_post_likes(self) -> int:
"""Returns the number of likes this user's posts have accumulated."""
diff --git a/templates/pages/user.html b/templates/pages/user.html
index 792fdad..d4a526a 100644
--- a/templates/pages/user.html
+++ b/templates/pages/user.html
@@ -4,7 +4,8 @@
{{ user.name }}
{% if current_user.is_admin or current_user == user %}
-
+
{% endif %}
{% with seen_text = user.get_last_seen_text() %}
{% if seen_text == 'Online now!' %}
@@ -20,7 +21,9 @@
alt="{{ user.username }}'s Profile Picture">
Registered {{ user.get_registration_delta() }} ago
- {# 0 likes
#}
+ {% with like_count = user.get_post_likes() %}
+ {{ like_count }} like{{ like_count|pluralize }}
+ {% endwith %}
{% with post_count = user.get_post_count() %}
{{ post_count }} post{{ post_count|pluralize }}
{% endwith %}
From 2c8d97559d891bcbecf4dede30a2caffd0cf1ec6 Mon Sep 17 00:00:00 2001
From: Xevion
Date: Wed, 30 Mar 2022 01:45:44 -0500
Subject: [PATCH 33/33] Show divider only when post form is displayed, add post
count text.
---
templates/pages/feed.html | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/templates/pages/feed.html b/templates/pages/feed.html
index 442ddb0..b7d6b1a 100644
--- a/templates/pages/feed.html
+++ b/templates/pages/feed.html
@@ -10,9 +10,13 @@
{{ render_field(form.text, show_label=False) }}
+
+
{% endif %}
-
+ {% with post_count = posts|length %}
+ Showing {{ post_count }} post{{ post_count|pluralize }}.
+ {% endwith %}
{% for post in posts %}