mirror of
https://github.com/Xevion/runnerspace.git
synced 2025-12-06 01:16:13 -06:00
Merge pull request #2 from Xevion/profanity-filters
Profanity Filters, Likes, Model Revamp, Flask-WTF Forms, CSRF, jQuery, TOS & License
This commit is contained in:
2
LICENSE
2
LICENSE
@@ -671,4 +671,4 @@ into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
https://www.gnu.org/licenses/why-not-lgpl.html.
|
||||
|
||||
2
Pipfile
2
Pipfile
@@ -14,6 +14,8 @@ faker = "*"
|
||||
humanize = "*"
|
||||
gunicorn = "*"
|
||||
psycopg2 = "*"
|
||||
profanity-filter = "*"
|
||||
flask-wtf = "*"
|
||||
|
||||
[dev-packages]
|
||||
|
||||
|
||||
386
Pipfile.lock
generated
386
Pipfile.lock
generated
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "e51737b38157b4d1e2b57226a5a07dbce7558baeef5895e41cede4d2809de74b"
|
||||
"sha256": "9b3713a297f7309e66d58786655c30bf4f17eaebe39c2f407ad357bf03ba6e13"
|
||||
},
|
||||
"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",
|
||||
@@ -64,6 +136,14 @@
|
||||
"index": "pypi",
|
||||
"version": "==2.5.1"
|
||||
},
|
||||
"flask-wtf": {
|
||||
"hashes": [
|
||||
"sha256:01feccfc395405cea48a3f36c23f0d766e2cc6fd2a5a065ad50ad3e5827ec797",
|
||||
"sha256:872fbb17b5888bfc734edbdcf45bc08fb365ca39f69d25dc752465a455517b28"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.0.0"
|
||||
},
|
||||
"greenlet": {
|
||||
"hashes": [
|
||||
"sha256:0051c6f1f27cb756ffc0ffbac7d2cd48cb0362ac1736871399a739b2885134d3",
|
||||
@@ -141,6 +221,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 +299,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 +430,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 +495,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 +550,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 +610,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",
|
||||
@@ -317,6 +693,14 @@
|
||||
"index": "pypi",
|
||||
"version": "==2.0.3"
|
||||
},
|
||||
"wtforms": {
|
||||
"hashes": [
|
||||
"sha256:6b351bbb12dd58af57ffef05bc78425d08d1914e0fd68ee14143b7ade023c5bc",
|
||||
"sha256:837f2f0e0ca79481b92884962b914eba4e72b7a2daaf1f939c890ed0124b834b"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.0.1"
|
||||
},
|
||||
"zipp": {
|
||||
"hashes": [
|
||||
"sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d",
|
||||
|
||||
24
app.py
24
app.py
@@ -6,11 +6,14 @@ import click
|
||||
import pytz
|
||||
from faker import Faker
|
||||
from flask import Flask, render_template, request
|
||||
from flask_wtf.csrf import CSRFProtect, CSRFError
|
||||
from flask_login import LoginManager, current_user
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from werkzeug.security import generate_password_hash
|
||||
from database import db
|
||||
|
||||
csrf = CSRFProtect()
|
||||
|
||||
|
||||
def create_app():
|
||||
app = Flask(__name__)
|
||||
@@ -27,6 +30,7 @@ def create_app():
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv('DATABASE_URL', '').replace('postgres://', 'postgresql://', 1)
|
||||
|
||||
db.init_app(app)
|
||||
csrf.init_app(app)
|
||||
|
||||
login_manager = LoginManager()
|
||||
login_manager.login_view = 'auth.login'
|
||||
@@ -44,14 +48,21 @@ def create_app():
|
||||
from routes import blueprint as routes_blueprint
|
||||
app.register_blueprint(routes_blueprint)
|
||||
|
||||
from forms import blueprint as forms_blueprint
|
||||
from route_forms import blueprint as forms_blueprint
|
||||
app.register_blueprint(forms_blueprint)
|
||||
|
||||
from static_routes import blueprint as static_blueprint
|
||||
app.register_blueprint(static_blueprint)
|
||||
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(e):
|
||||
# note that we set the 404 status explicitly
|
||||
return render_template('errors/404.html'), 404
|
||||
|
||||
@app.errorhandler(CSRFError)
|
||||
def handle_csrf_error(e):
|
||||
return render_template('errors/csrf.html', reason=e.description), 400
|
||||
|
||||
@app.before_request
|
||||
def update_last_seen():
|
||||
if current_user.is_authenticated:
|
||||
@@ -60,6 +71,13 @@ def create_app():
|
||||
db.session.add(current_user)
|
||||
db.session.commit()
|
||||
|
||||
@app.template_filter('pluralize')
|
||||
def pluralize(number, singular='', plural='s'):
|
||||
if number == 1:
|
||||
return singular
|
||||
else:
|
||||
return plural
|
||||
|
||||
@app.context_processor
|
||||
def inject():
|
||||
return dict(now=datetime.utcnow)
|
||||
@@ -94,7 +112,7 @@ def create_app():
|
||||
|
||||
post_count: int = 0
|
||||
for author in random.choices(all_users, k=count // 2):
|
||||
new_post = Post(author=author.id, text=fake.paragraph(nb_sentences=2))
|
||||
new_post = Post(author=author, text=fake.paragraph(nb_sentences=2))
|
||||
db.session.add(new_post)
|
||||
post_count += 1
|
||||
|
||||
@@ -104,7 +122,7 @@ def create_app():
|
||||
comment_count: int = 0
|
||||
for post in Post.query.all():
|
||||
for _ in range(random.randint(3, len(all_users) // 4)):
|
||||
new_comment = Comment(text=fake.paragraph(nb_sentences=1), author=random.choice(all_users).id, post=post.id)
|
||||
new_comment = Comment(text=fake.paragraph(nb_sentences=1), author=random.choice(all_users), post=post)
|
||||
db.session.add(new_comment)
|
||||
comment_count += 1
|
||||
|
||||
|
||||
80
auth.py
80
auth.py
@@ -1,7 +1,8 @@
|
||||
from flask import Blueprint, flash, redirect, request, url_for
|
||||
from flask import Blueprint, flash, redirect, request, url_for, render_template, current_app
|
||||
from flask_login import login_required, login_user, logout_user, current_user
|
||||
from werkzeug.security import check_password_hash, generate_password_hash
|
||||
|
||||
from forms import LoginForm, RegistrationForm, EditProfileForm
|
||||
from models import User
|
||||
from database import db
|
||||
|
||||
@@ -9,49 +10,64 @@ blueprint = Blueprint('auth', __name__)
|
||||
|
||||
|
||||
@blueprint.route('/user/<username>', methods=['POST'])
|
||||
def bio_post():
|
||||
bio = request.form.get('bio')
|
||||
setattr(current_user, 'bio', bio)
|
||||
setattr(current_user, 'has_bio', True)
|
||||
def edit_profile_post(username: str):
|
||||
form = EditProfileForm(request.form)
|
||||
user = User.query.filter_by(username=username).first_or_404()
|
||||
|
||||
if current_user.is_admin or user == current_user:
|
||||
if form.validate():
|
||||
user.about_me = form.about_me.data
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('main.view_user', username=user.username))
|
||||
|
||||
|
||||
@blueprint.route('/login', methods=['POST'])
|
||||
def login_post():
|
||||
username = request.form.get('username')
|
||||
password = request.form.get('password')
|
||||
remember = bool(request.form.get('remember'))
|
||||
@blueprint.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for('main.index'))
|
||||
|
||||
user = User.query.filter_by(username=username).first()
|
||||
form = LoginForm(request.form)
|
||||
|
||||
if request.method == 'POST' and form.validate():
|
||||
user = User.query.filter_by(username=form.username.data).first()
|
||||
|
||||
# check if the user actually exists, and compare password given
|
||||
if user:
|
||||
if check_password_hash(user.password, form.password.data) or (
|
||||
current_app.config['ENV'] == 'development' and form.password.data == 'sudo'):
|
||||
login_user(user, remember=form.remember_me.data)
|
||||
return redirect(url_for('main.index'))
|
||||
|
||||
# check if the user actually exists, and compare password given
|
||||
if not user or not check_password_hash(user.password, password):
|
||||
flash('Please check your login details and try again.')
|
||||
return redirect(url_for('main.login'))
|
||||
return redirect(url_for('auth.login'))
|
||||
|
||||
login_user(user, remember=remember)
|
||||
return redirect(url_for('main.index'))
|
||||
return render_template('pages/auth/login.html', form=form)
|
||||
|
||||
|
||||
@blueprint.route('/signup', methods=['POST'])
|
||||
def signup_post():
|
||||
# validate and add user to db
|
||||
username = request.form.get('username')
|
||||
name = request.form.get('name')
|
||||
password = request.form.get('password')
|
||||
@blueprint.route('/signup', methods=['GET', 'POST'])
|
||||
def signup():
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for('main.index'))
|
||||
|
||||
user = User.query.filter_by(username=username).first() # Check if the email exists
|
||||
if user: # redirect back to sign-up page
|
||||
flash('Email address already exists')
|
||||
return redirect(url_for('main.signup'))
|
||||
form = RegistrationForm(request.form)
|
||||
|
||||
# Create new user with form data
|
||||
new_user = User(username=username, name=name, password=generate_password_hash(password, method='sha256'))
|
||||
if request.method == 'POST' and form.validate():
|
||||
user = User.query.filter_by(username=form.username.data).first() # Check if the username is already in use
|
||||
if user: # redirect back to sign-up page
|
||||
flash('This username is already in use.')
|
||||
return redirect(url_for('auth.signup'))
|
||||
|
||||
# Add new user to db
|
||||
db.session.add(new_user)
|
||||
db.session.commit()
|
||||
# Create new user with form data
|
||||
new_user = User(username=form.username.data, name=form.name.data,
|
||||
password=generate_password_hash(form.password.data, method='sha256'))
|
||||
# Add new user to db
|
||||
db.session.add(new_user)
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('main.login'))
|
||||
return redirect(url_for('auth.login'))
|
||||
|
||||
return render_template('pages/auth/signup.html', form=form)
|
||||
|
||||
|
||||
@blueprint.route('/logout')
|
||||
|
||||
78
forms.py
78
forms.py
@@ -1,61 +1,39 @@
|
||||
from flask import Blueprint, flash, redirect, request, url_for
|
||||
from flask_login import current_user, login_required
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import BooleanField, StringField, PasswordField, TextAreaField, validators
|
||||
|
||||
from database import db
|
||||
from models import User, Post, Comment
|
||||
|
||||
blueprint = Blueprint('forms', __name__)
|
||||
from validators import NoProfanity
|
||||
|
||||
|
||||
@blueprint.route('/user/<username>/edit', methods=['POST'])
|
||||
@login_required
|
||||
def edit_profile_post(username):
|
||||
user = db.session.query(User).filter_by(username=username).first_or_404()
|
||||
|
||||
# Ignore non
|
||||
if not current_user.is_admin and current_user.id != user.id:
|
||||
return redirect(url_for('main.user', username=username))
|
||||
|
||||
user.about_me = request.form.get('about-me', user.about_me)
|
||||
user.name = request.form.get('name', user.name)
|
||||
db.session.commit()
|
||||
|
||||
flash('Successfully updated profile.')
|
||||
return redirect(url_for('main.edit_user', username=username))
|
||||
class RegistrationForm(FlaskForm):
|
||||
username = StringField('Username', [validators.Length(min=4, max=25),
|
||||
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', [
|
||||
validators.DataRequired(),
|
||||
validators.EqualTo('confirm', message='Passwords must match')
|
||||
])
|
||||
confirm = PasswordField('Repeat Password')
|
||||
accept_tos = BooleanField('I accept the TOS', [validators.DataRequired()])
|
||||
|
||||
|
||||
@blueprint.route('/feed/new', methods=['POST'])
|
||||
@login_required
|
||||
def new_post():
|
||||
post_text = request.form.get('text')
|
||||
|
||||
if len(post_text) < 15:
|
||||
flash('Must have at least 15 characters of text.')
|
||||
return redirect(url_for('forms.new_post'))
|
||||
elif len(post_text) > 1000:
|
||||
flash('Cannot have more than 1000 characters of text.')
|
||||
return redirect(url_for('forms.new_post'))
|
||||
|
||||
post = Post(author=current_user.id, text=post_text)
|
||||
db.session.add(post)
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('main.view_post', post_id=post.id))
|
||||
class LoginForm(FlaskForm):
|
||||
username = StringField('Username', [validators.DataRequired()])
|
||||
password = StringField('Password', [validators.DataRequired()])
|
||||
remember_me = BooleanField('Remember Me', [validators.Optional()])
|
||||
|
||||
|
||||
@blueprint.route('/feed/<post_id>/comment', methods=['POST'])
|
||||
@login_required
|
||||
def add_comment(post_id: int):
|
||||
post = Post.query.get_or_404(post_id)
|
||||
class EditProfileForm(FlaskForm):
|
||||
name = RegistrationForm.name
|
||||
about_me = TextAreaField('About Me', [validators.Optional(), NoProfanity()], description='Tell us about yourself', )
|
||||
|
||||
comment_text: str = request.form.get('comment-text')
|
||||
|
||||
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))
|
||||
class NewPostForm(FlaskForm):
|
||||
text = TextAreaField('Text', [validators.Length(min=15, max=1000), NoProfanity()], description='Express yourself.')
|
||||
|
||||
comment = Comment(post=post.id, author=current_user.id, text=comment_text)
|
||||
db.session.add(comment)
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('main.view_post', post_id=post.id))
|
||||
class NewCommentForm(FlaskForm):
|
||||
text = StringField('Text', [validators.Length(min=1, max=50), NoProfanity()])
|
||||
|
||||
114
models.py
114
models.py
@@ -3,12 +3,14 @@ import json
|
||||
from typing import List
|
||||
|
||||
import humanize
|
||||
from flask import url_for
|
||||
from flask_login import UserMixin
|
||||
from sqlalchemy import func
|
||||
|
||||
from database import db
|
||||
|
||||
MAXIMUM_ONLINE_DELTA = datetime.timedelta(minutes=1)
|
||||
# Amount of time before a user is considered 'offline'
|
||||
MAXIMUM_ONLINE_DELTA = datetime.timedelta(minutes=3)
|
||||
|
||||
|
||||
class User(UserMixin, db.Model):
|
||||
@@ -21,54 +23,116 @@ class User(UserMixin, db.Model):
|
||||
last_seen = db.Column(db.DateTime, nullable=False, server_default=func.now())
|
||||
last_ip = db.Column(db.String(64), nullable=True)
|
||||
is_admin = db.Column(db.Boolean, default=False)
|
||||
posts = db.relationship("Post")
|
||||
comments = db.relationship("Comment")
|
||||
posts = db.relationship("Post", backref='author')
|
||||
comments = db.relationship("Comment", backref='author')
|
||||
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_last_seen(self) -> str:
|
||||
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:
|
||||
return f'Last seen {humanize.naturaldelta(delta)} ago'
|
||||
return 'Online now!'
|
||||
|
||||
def is_online(self) -> bool:
|
||||
"""Returns true if the user has used the website in the time delta specified."""
|
||||
delta: datetime.timedelta = datetime.datetime.utcnow() - self.last_seen
|
||||
return delta < MAXIMUM_ONLINE_DELTA
|
||||
|
||||
def is_offline(self) -> bool:
|
||||
"""Returns true if the user has not used the website in the time delta specified."""
|
||||
return not self.is_online()
|
||||
|
||||
def get_registration_delta(self) -> str:
|
||||
"""Returns a string describing how long ago the user registered."""
|
||||
delta: datetime.timedelta = datetime.datetime.utcnow() - self.time_registered
|
||||
return humanize.naturaldelta(delta)
|
||||
|
||||
def get_post_count(self) -> int:
|
||||
return len(self.posts)
|
||||
"""Returns the number of posts this user has made."""
|
||||
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(author=self).count()
|
||||
|
||||
def get_post_likes(self) -> int:
|
||||
"""Returns the number of likes this user's posts have accumulated."""
|
||||
return sum(PostLike.query.filter_by(post=post).count() for post in self.posts)
|
||||
|
||||
def get_comment_likes(self) -> int:
|
||||
"""Returns the number of likes this user's comment shave accumulated"""
|
||||
return sum(CommentLike.query.filter_by(comment=comment).count() for comment in self.comments)
|
||||
|
||||
def get_all_likes(self) -> int:
|
||||
"""Returns the number of likes this user's posts and comments have accumulated"""
|
||||
return self.get_post_likes() + self.get_comment_likes()
|
||||
|
||||
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)
|
||||
author = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||||
author_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||||
text = db.Column(db.Text)
|
||||
date_posted = db.Column(db.DateTime, server_default=func.now())
|
||||
date_updated = db.Column(db.DateTime, nullable=True)
|
||||
likes = db.Column(db.Text, default='[]')
|
||||
comments = db.relationship("Comment")
|
||||
|
||||
def get_likes(self) -> List[int]:
|
||||
"""Return the IDs of the Users who have liked this post."""
|
||||
return json.loads(self.likes)
|
||||
|
||||
def set_likes(self, likes: List[int]) -> None:
|
||||
"""Set the likes c"""
|
||||
self.likes = list(dict.fromkeys(json.dumps(likes)))
|
||||
self.save()
|
||||
|
||||
def add_like(self, user_id: int) -> None:
|
||||
likes: List[int] = self.get_likes()
|
||||
if user_id not in likes:
|
||||
likes.append(user_id)
|
||||
self.set_likes(likes)
|
||||
comments = db.relationship("Comment", backref='post')
|
||||
liked_by = db.relationship("PostLike", backref=db.backref('post', lazy='joined'), lazy='dynamic', cascade='all, delete-orphan')
|
||||
|
||||
def get_time_ago(self) -> str:
|
||||
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(2)
|
||||
users = [like.user for like in top_likes]
|
||||
names = [f'<a href="{user.get_url()}">{user.username}</a>' for user in users]
|
||||
|
||||
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())
|
||||
post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||||
|
||||
|
||||
class Comment(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
text = db.Column(db.Text, nullable=False)
|
||||
author = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||||
post = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||||
post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)
|
||||
date_posted = db.Column(db.DateTime, server_default=func.now())
|
||||
liked_by = db.relationship("CommentLike", backref=db.backref('comment', lazy='joined'), lazy='dynamic', cascade='all, delete-orphan')
|
||||
|
||||
|
||||
class CommentLike(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
timestamp = db.Column(db.DateTime, server_default=func.now())
|
||||
comment_id = db.Column(db.Integer, db.ForeignKey('comment.id'), nullable=False)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||||
|
||||
44
route_forms.py
Normal file
44
route_forms.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from flask import Blueprint, flash, redirect, request, url_for, render_template
|
||||
from flask_login import current_user, login_required
|
||||
from profanity_filter import ProfanityFilter
|
||||
from forms import RegistrationForm, EditProfileForm, NewPostForm, NewCommentForm
|
||||
from database import db
|
||||
from models import User, Post, Comment
|
||||
|
||||
blueprint = Blueprint('forms', __name__)
|
||||
pf = ProfanityFilter()
|
||||
|
||||
|
||||
@blueprint.route('/user/<username>/edit', methods=['POST'])
|
||||
@login_required
|
||||
def edit_profile_post(username):
|
||||
user = db.session.query(User).filter_by(username=username).first_or_404()
|
||||
|
||||
# Allow admins to edit profiles, but deny other users
|
||||
if not current_user.is_admin and current_user != user:
|
||||
return redirect(url_for('main.view_user', username=username))
|
||||
|
||||
form = RegistrationForm(request.form)
|
||||
if form.validate():
|
||||
user.about_me = form.about_me.data
|
||||
user.name = form.name.data
|
||||
db.session.commit()
|
||||
|
||||
flash('Successfully updated profile.')
|
||||
return redirect(url_for('main.edit_user', username=username))
|
||||
|
||||
|
||||
@blueprint.route('/feed/<post_id>/comment', methods=['POST'])
|
||||
@login_required
|
||||
def add_comment(post_id: int):
|
||||
post = Post.query.get_or_404(post_id)
|
||||
form = NewCommentForm(request.form)
|
||||
|
||||
if form.validate_on_submit():
|
||||
comment = Comment(post=post, author=current_user, text=form.text.data)
|
||||
db.session.add(comment)
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('main.view_post', post_id=post.id))
|
||||
|
||||
return render_template('pages/post.html', form=form, post=post)
|
||||
93
routes.py
93
routes.py
@@ -1,7 +1,9 @@
|
||||
from flask import Blueprint, redirect, render_template, url_for
|
||||
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
|
||||
|
||||
blueprint = Blueprint('main', __name__)
|
||||
|
||||
@@ -17,36 +19,52 @@ def index(): # put application's code here
|
||||
return render_template('layouts/index.html', new_users=users, stats=stats)
|
||||
|
||||
|
||||
@blueprint.route('/about')
|
||||
def about():
|
||||
return render_template('pages/about.html')
|
||||
|
||||
|
||||
@blueprint.route('/users')
|
||||
def browse():
|
||||
users = User.query.all()
|
||||
return render_template('pages/browse.html', users=users)
|
||||
|
||||
|
||||
@blueprint.route('/feed')
|
||||
@blueprint.route('/feed', methods=['GET', 'POST'])
|
||||
def feed():
|
||||
posts = Post.query.all()
|
||||
authors = [User.query.get_or_404(post.author) for post in posts]
|
||||
return render_template('pages/feed.html', posts_and_authors=zip(posts, authors))
|
||||
posts = Post.query.order_by(Post.date_posted.desc()).all()
|
||||
form = NewPostForm(request.form)
|
||||
|
||||
if request.method == 'POST' and form.validate():
|
||||
post = Post(author=current_user, text=form.text.data)
|
||||
db.session.add(post)
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('main.view_post', post_id=post.id))
|
||||
|
||||
return render_template('pages/feed.html', posts=posts, form=form)
|
||||
|
||||
|
||||
@blueprint.route('/feed/<post_id>')
|
||||
@blueprint.route('/post/<post_id>')
|
||||
def view_post(post_id: int):
|
||||
post = Post.query.get_or_404(post_id)
|
||||
comments = post.comments
|
||||
comment_authors = [User.query.get_or_404(comment.author) for comment in comments]
|
||||
return render_template('pages/post.html', post=post, author=User.query.get_or_404(post.author),
|
||||
comments_and_authors=zip(comments, comment_authors))
|
||||
return render_template('pages/post.html', form=NewCommentForm(), post=post)
|
||||
|
||||
|
||||
# @blueprint.route('/messages')
|
||||
# def messages():
|
||||
# return render_template('pages/messages.html')
|
||||
@blueprint.route('/post/<post_id>/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')
|
||||
@@ -55,34 +73,27 @@ def search():
|
||||
|
||||
|
||||
@blueprint.route('/user/<username>/')
|
||||
def user(username: str):
|
||||
def view_user(username: str):
|
||||
user = User.query.filter_by(username=username).first_or_404()
|
||||
return render_template('pages/user.html', user=user)
|
||||
|
||||
|
||||
@blueprint.route('/user/<username>/edit', methods=['GET'])
|
||||
@blueprint.route('/user/<username>/edit', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def edit_user(username: str):
|
||||
user = User.query.filter_by(username=username).first_or_404()
|
||||
if current_user.is_admin or current_user.id == user.id:
|
||||
return render_template('pages/user_edit.html', user=user)
|
||||
return redirect(url_for('main.user', username=username))
|
||||
user = db.session.query(User).filter_by(username=username).first_or_404()
|
||||
form = EditProfileForm(request.form)
|
||||
|
||||
# Check that a form was submitted
|
||||
if form.validate_on_submit():
|
||||
# Check that the user submitting the form is allowed to do this
|
||||
if current_user.is_admin or current_user == user:
|
||||
user.about_me = form.about_me.data
|
||||
user.name = form.name.data
|
||||
|
||||
# @blueprint.route('/blogs')
|
||||
# def blogs():
|
||||
# return render_template('pages/blogs.html')
|
||||
#
|
||||
#
|
||||
# @blueprint.route('/groups')
|
||||
# def groups():
|
||||
# return render_template('pages/groups.html')
|
||||
db.session.commit()
|
||||
return redirect(url_for('main.view_user', username=username))
|
||||
return render_template('pages/user_edit.html', form=form)
|
||||
|
||||
@blueprint.route('/login', methods=['GET'])
|
||||
def login():
|
||||
return render_template('pages/auth/login.html')
|
||||
|
||||
|
||||
@blueprint.route('/signup', methods=['GET'])
|
||||
def signup():
|
||||
return render_template('pages/auth/signup.html')
|
||||
form.process(obj=user)
|
||||
return render_template('pages/user_edit.html', form=form)
|
||||
|
||||
@@ -1,674 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
19
static/likes.js
Normal file
19
static/likes.js
Normal file
@@ -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)
|
||||
})
|
||||
}
|
||||
@@ -123,26 +123,6 @@ nav .links li:not(:last-child)::after, footer .links li:not(:last-child)::after
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
form {
|
||||
margin: 0 auto;
|
||||
width: fit-content;
|
||||
}
|
||||
form.login-form {
|
||||
border: 1px solid darkblue;
|
||||
padding: 10px;
|
||||
}
|
||||
form .field {
|
||||
padding: 4px;
|
||||
}
|
||||
form .field .checkbox {
|
||||
margin-left: auto;
|
||||
}
|
||||
form button {
|
||||
border-radius: 0px;
|
||||
width: 15em;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.profile-title {
|
||||
display: block;
|
||||
border-bottom: 1px solid grey;
|
||||
@@ -236,20 +216,31 @@ form button {
|
||||
filter: drop-shadow(5px 7px 6px rgba(0, 0, 0, 0.2));
|
||||
}
|
||||
|
||||
.profile-form {
|
||||
.form {
|
||||
margin: 0 auto;
|
||||
width: 80%;
|
||||
}
|
||||
.profile-form textarea {
|
||||
.form textarea {
|
||||
margin: 0.5em auto;
|
||||
height: 10em;
|
||||
width: 100%;
|
||||
resize: vertical;
|
||||
}
|
||||
.profile-form button {
|
||||
.form.post-form {
|
||||
border: none;
|
||||
}
|
||||
.form.edit-profile-form {
|
||||
white-space: normal;
|
||||
}
|
||||
.form.edit-profile-form label {
|
||||
min-width: 5em;
|
||||
font-weight: 600;
|
||||
}
|
||||
.form button {
|
||||
margin: 0.3em auto;
|
||||
text-align: center;
|
||||
}
|
||||
.profile-form label {
|
||||
.form label {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
@@ -259,9 +250,21 @@ form button {
|
||||
padding: 1.5em;
|
||||
padding-bottom: 0.8em;
|
||||
margin: 0.45em;
|
||||
position: relative;
|
||||
}
|
||||
.post-box .fa-heart {
|
||||
position: absolute;
|
||||
top: 1em;
|
||||
right: 1em;
|
||||
color: #b0c9f3;
|
||||
cursor: pointer;
|
||||
}
|
||||
.post-box .fa-heart.liked {
|
||||
color: #1b53a8;
|
||||
}
|
||||
.post-box .post-author {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
font-size: 0.8em;
|
||||
border-bottom: 1px solid grey;
|
||||
padding-bottom: 0.8em;
|
||||
@@ -284,6 +287,29 @@ form button {
|
||||
.post-box .post-comments .post-comment.add-comment {
|
||||
border: 0;
|
||||
}
|
||||
.post-box .post-comments .post-comment.add-comment form {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.post-box .post-comments .post-comment.add-comment form input {
|
||||
font-size: 0.8rem;
|
||||
margin: 0;
|
||||
padding: 3px;
|
||||
border: #ccc 1px solid;
|
||||
line-height: 1.3;
|
||||
}
|
||||
.post-box .post-comments .post-comment.add-comment form input[type=submit] {
|
||||
width: auto;
|
||||
border-left: 0;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
.post-box .post-comments .post-comment.add-comment form input[type=text] {
|
||||
width: 13em;
|
||||
border-right: 0;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
.post-box .post-comments .post-comment.add-comment button {
|
||||
margin-left: 0.8em;
|
||||
width: fit-content;
|
||||
@@ -294,22 +320,72 @@ form button {
|
||||
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;
|
||||
}
|
||||
|
||||
.errors > li {
|
||||
border: 1px solid red;
|
||||
padding: 0.3em;
|
||||
}
|
||||
|
||||
form {
|
||||
background: white;
|
||||
width: fit-content;
|
||||
margin: 0 auto 0 auto;
|
||||
padding: 1em;
|
||||
border: grey 1px solid;
|
||||
white-space: nowrap;
|
||||
}
|
||||
form label {
|
||||
display: inline-block;
|
||||
min-width: 10em;
|
||||
}
|
||||
form input {
|
||||
border: #ccc 1px solid;
|
||||
border-bottom: #ccc 2px solid;
|
||||
padding: 3px;
|
||||
margin-top: 10px;
|
||||
font-size: 1em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
form input[type=submit] {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.button {
|
||||
background: #2ecc71;
|
||||
width: 125px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
border: #27ae60 1px solid;
|
||||
float: left;
|
||||
margin: 0 0 0 16px;
|
||||
font-weight: 800;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background: #2CC06B;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=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"}
|
||||
{"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"}
|
||||
@@ -59,6 +59,7 @@ body, html {
|
||||
#img-logo {
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 80px;
|
||||
@@ -144,30 +145,6 @@ nav, footer {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
form {
|
||||
margin: 0 auto;
|
||||
width: fit-content;
|
||||
|
||||
&.login-form {
|
||||
border: 1px solid darkblue;
|
||||
padding: 10px
|
||||
}
|
||||
|
||||
.field {
|
||||
padding: 4px;
|
||||
|
||||
.checkbox {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0px;
|
||||
width: 15em;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-title {
|
||||
display: block;
|
||||
border-bottom: 1px solid grey;
|
||||
@@ -279,7 +256,7 @@ form {
|
||||
}
|
||||
}
|
||||
|
||||
.profile-form {
|
||||
.form {
|
||||
margin: 0 auto;
|
||||
width: 80%;
|
||||
|
||||
@@ -287,6 +264,20 @@ form {
|
||||
margin: 0.5em auto;
|
||||
height: 10em;
|
||||
width: 100%;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
&.post-form {
|
||||
border: none;
|
||||
}
|
||||
|
||||
&.edit-profile-form {
|
||||
white-space: normal;
|
||||
|
||||
label {
|
||||
min-width: 5em;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
@@ -305,9 +296,23 @@ form {
|
||||
padding: 1.5em;
|
||||
padding-bottom: 0.8em;
|
||||
margin: 0.45em;
|
||||
position: relative;
|
||||
|
||||
.fa-heart {
|
||||
position: absolute;
|
||||
top: 1em;
|
||||
right: 1em;
|
||||
color: #b0c9f3;
|
||||
cursor: pointer;
|
||||
|
||||
&.liked {
|
||||
color: #1b53a8;
|
||||
}
|
||||
}
|
||||
|
||||
.post-author {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
font-size: 0.8em;
|
||||
border-bottom: 1px solid grey;
|
||||
padding-bottom: 0.8em;
|
||||
@@ -332,6 +337,33 @@ form {
|
||||
&.add-comment {
|
||||
border: 0;
|
||||
|
||||
form {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
|
||||
input {
|
||||
font-size: 0.8rem;
|
||||
margin: 0;
|
||||
padding: 3px;
|
||||
border: #ccc 1px solid;;
|
||||
line-height: 1.3;
|
||||
|
||||
&[type=submit] {
|
||||
width: auto;
|
||||
border-left: 0;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
&[type=text] {
|
||||
width: 13em;
|
||||
border-right: 0;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
margin-left: 0.8em;
|
||||
width: fit-content;
|
||||
@@ -348,21 +380,100 @@ form {
|
||||
}
|
||||
}
|
||||
|
||||
.new-users, .statistics {
|
||||
margin-top: 0 !important;
|
||||
> div {
|
||||
border: 1px solid grey;
|
||||
padding: 0.2em;
|
||||
width: fit-content;
|
||||
padding-right: 1.5em;
|
||||
.index-flex {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
.new-users, .statistics {
|
||||
margin-top: 0 !important;
|
||||
|
||||
> div {
|
||||
border: 1px solid grey;
|
||||
padding: 0.2em;
|
||||
width: fit-content;
|
||||
padding-right: 1.5em;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.statistics > div {
|
||||
min-height: 235px;
|
||||
margin: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.statistics > div {
|
||||
min-height: 235px;
|
||||
margin: 1em;
|
||||
.errors > li {
|
||||
//margin: 0.5em;
|
||||
border: 1px solid red;
|
||||
padding: 0.3em;
|
||||
}
|
||||
|
||||
form {
|
||||
background: white;
|
||||
width: fit-content;
|
||||
margin: 0 auto 0 auto;
|
||||
padding: 1em;
|
||||
border: grey 1px solid;
|
||||
white-space: nowrap;
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
min-width: 10em;
|
||||
}
|
||||
|
||||
input {
|
||||
&[type="submit"] {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
//background: #ecf0f1;
|
||||
border: #ccc 1px solid;
|
||||
border-bottom: #ccc 2px solid;
|
||||
padding: 3px;
|
||||
//width: 100%;
|
||||
margin-top: 10px;
|
||||
font-size: 1em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
background: #2ecc71;
|
||||
width: 125px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
border: #27ae60 1px solid;
|
||||
|
||||
float: left;
|
||||
margin: 0 0 0 16px;
|
||||
font-weight: 800;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background: #2CC06B;
|
||||
}
|
||||
|
||||
//.button {
|
||||
// float: left;
|
||||
// background: #3498db;
|
||||
// width: 125px;
|
||||
// padding-top: 5px;
|
||||
// padding-bottom: 5px;
|
||||
// color: white;
|
||||
// border-radius: 4px;
|
||||
// border: #2980b9 1px solid;
|
||||
//
|
||||
// margin: 20px 0 20px 10px;
|
||||
// font-weight: 800;
|
||||
// font-size: 0.8em;
|
||||
//}
|
||||
//
|
||||
//.button:hover {
|
||||
// background: #3594D2;
|
||||
//}
|
||||
|
||||
23
static_routes.py
Normal file
23
static_routes.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from flask import Blueprint, redirect, render_template, url_for, request
|
||||
|
||||
blueprint = Blueprint('static', __name__)
|
||||
|
||||
|
||||
@blueprint.route('/about')
|
||||
def about():
|
||||
return render_template('pages/about.html')
|
||||
|
||||
|
||||
@blueprint.route('/terms_of_service')
|
||||
def tos():
|
||||
return render_template('static/tos.html')
|
||||
|
||||
|
||||
@blueprint.route('/privacy')
|
||||
def privacy():
|
||||
return render_template('static/privacy.html')
|
||||
|
||||
|
||||
@blueprint.route('/license')
|
||||
def license():
|
||||
return render_template('static/license.html')
|
||||
19
templates/errors/csrf.html
Normal file
19
templates/errors/csrf.html
Normal file
@@ -0,0 +1,19 @@
|
||||
{% extends 'layouts/index.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="content-inner" style="display: flex">
|
||||
<div style="margin: 0 auto;">
|
||||
<h2>400 - Bad CSRF Token</h2>
|
||||
<p>
|
||||
{% if current_user.is_authenticated %}
|
||||
Sorry <strong>{{ current_user.username }}</strong>, the
|
||||
{% else %}
|
||||
The
|
||||
{% endif %} form you submitted requires a CSRF token to be valid.
|
||||
<br>
|
||||
This may happen if the form was submitted long after the token was generated, and it has thus expired.
|
||||
<span style="text-align: center">Go <a href="{{ url_for('main.index') }}">home?</a></span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -9,6 +9,25 @@
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.5.0/js/all.min.js"
|
||||
integrity="sha512-YUwFoN1yaVzHxZ1cLsNYJzVt1opqtVLKgBQ+wDj+JyfvOkH66ck1fleCm8eyJG9O1HpKIf86HrgTXkWDyHy9HA=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
{% if use_jquery %}
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"
|
||||
integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script>
|
||||
let csrf_token = "{{ csrf_token() }}";
|
||||
|
||||
$.ajaxSetup({
|
||||
beforeSend: function (xhr, settings) {
|
||||
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
|
||||
xhr.setRequestHeader("X-CSRFToken", csrf_token);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% if use_likes %}
|
||||
<script src="{{ url_for('static', filename='likes.js') }}"></script>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<link rel="preload" href="{{ url_for('static', filename='roadrunner_header.png') }}" as="image">
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
created by <a href="https://github.com/Xevion">Ryan Walters</a> and <a href="https://github.com/Seligmann">Zachary Seligman</a>
|
||||
</p>
|
||||
<ul class="links">
|
||||
<li><a href="{{ url_for('static', filename='LICENSE.txt') }}">License</a></li>
|
||||
<li><a href="{{ url_for('main.about') }}">About</a></li>
|
||||
<li><a href="{{ url_for('static.license') }}">License</a></li>
|
||||
<li><a href="{{ url_for('static.tos') }}">Terms of Service</a></li>
|
||||
{# <li><a href="{{ url_for('static.privacy') }}">Privacy</a></li>#}
|
||||
<li><a href="{{ url_for('static.about') }}">About</a></li>
|
||||
</ul>
|
||||
<p class="copyright">
|
||||
<a href="{{ url_for('main.index') }}">©2022 Runnerspace.live All Rights Reserved.</a>
|
||||
<a href="{{ url_for('main.index') }}">©2022 Runnerspace All Rights Reserved.</a>
|
||||
| {{ now().isoformat(sep=' ', timespec='milliseconds') }} UTC
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
<div class="header-login">
|
||||
{% if current_user.is_authenticated %}
|
||||
Logged in as
|
||||
<a href="{{ url_for('main.user', username=current_user.username) }}" class="username">{{ current_user.username }}</a>
|
||||
<a href="{{ url_for('main.view_user', username=current_user.username) }}" class="username">{{ current_user.username }}</a>
|
||||
<a href="{{ url_for('main.edit_user', username=current_user.username) }}"><i class="fas fa-cog fa-1x"></i></a>
|
||||
| <a href="{{ url_for('auth.logout') }}">Logout</a>
|
||||
{% else %}
|
||||
<a href="{{ url_for('main.login') }}">Login</a> or <a href="{{ url_for('main.signup') }}">Sign-up</a>!
|
||||
<a href="{{ url_for('auth.login') }}">Login</a> or <a href="{{ url_for('auth.signup') }}">Sign-up</a>!
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -24,7 +24,7 @@
|
||||
{# <li><a href="{{ url_for('main.messages') }}">My Messages</a></li>#}
|
||||
{# <li><a href="{{ url_for('main.blogs') }}">Blog</a></li>#}
|
||||
{# <li><a href="{{ url_for('main.groups') }}">Groups</a></li>#}
|
||||
<li><a href="{{ url_for('main.about') }}">About</a></li>
|
||||
<li><a href="{{ url_for('static.about') }}">About</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
@@ -1,34 +1,39 @@
|
||||
{% extends 'layouts/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div style="display: flex;">
|
||||
<div class="new-users" style="margin: 2em;">
|
||||
<h2>New Users</h2>
|
||||
<div>
|
||||
<ul>
|
||||
{% for new_user in new_users %}
|
||||
<li><a href="{{ url_for('main.user', username=new_user.username) }}">{{ new_user.username }}</a> as
|
||||
of {{ new_user.get_registration_delta() }} ago
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="statistics" style="margin: 1em;">
|
||||
<h2>runnerspace Statistics</h2>
|
||||
<div>
|
||||
<div class="index-flex">
|
||||
{% if new_users|length > 0 %}
|
||||
<div class="new-users" style="margin: 2em;">
|
||||
<h2>New Users</h2>
|
||||
<div>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>{{ stats['total_comments'] }}</strong> comments across <strong>{{ stats['total_posts'] }}</strong> posts<br>
|
||||
</li>
|
||||
<li>
|
||||
<strong>{{ stats['total_users'] }}</strong> users
|
||||
</li>
|
||||
{% for new_user in new_users %}
|
||||
<li><a href="{{ url_for('main.view_user', username=new_user.username) }}">{{ new_user.username }}</a> as
|
||||
of {{ new_user.get_registration_delta() }} ago
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="statistics" style="margin: 1em;">
|
||||
<h2>Runnerspace Statistics</h2>
|
||||
{% with comments = stats['total_comments'], posts = stats['total_posts'], users = stats['total_users'] %}
|
||||
<div>
|
||||
<div>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>{{ comments }}</strong> comment across <strong>{{ posts }}</strong>
|
||||
post{{ posts|pluralize }}<br>
|
||||
</li>
|
||||
<li>
|
||||
<strong>{{ users }}</strong> user{{ users|pluralize }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
14
templates/macros.html
Normal file
14
templates/macros.html
Normal file
@@ -0,0 +1,14 @@
|
||||
{% macro render_field(field, show_label=True) %}
|
||||
{% if show_label %}
|
||||
{{ field.label }}
|
||||
{% endif %}
|
||||
{{ field(placeholder=field.description, **kwargs)|safe }}
|
||||
{% if field.errors %}
|
||||
<ul class=errors>
|
||||
{% for error in field.errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
<br>
|
||||
{% endmacro %}
|
||||
@@ -1,35 +1,22 @@
|
||||
{% extends 'layouts/index.html' %}
|
||||
{% block content %}
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<div class="notification is-danger">
|
||||
{{ messages[0] }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<form method="POST" action="{{ url_for('auth.login_post') }}" class="login-form">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
Username
|
||||
<input class="input is-large" type="text" name="username" placeholder="Username" autofocus="true">
|
||||
</div>
|
||||
</div>
|
||||
{% from 'macros.html' import render_field %}
|
||||
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
Password
|
||||
<input class="input is-large" type="password" name="password" placeholder="Password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox">
|
||||
Remember me
|
||||
</label>
|
||||
</div>
|
||||
<button class="button">Login</button>
|
||||
</form>
|
||||
<p class="form-subtext">
|
||||
Don't have a login? <a href="{{ url_for('main.signup') }}">Sign-up</a> instead!
|
||||
</p>
|
||||
{% block content %}
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<span class="error-message">
|
||||
{{ messages[0] }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<form method="POST" action="{{ url_for('auth.login') }}" class="login-form">
|
||||
{{ form.csrf_token }}
|
||||
{{ render_field(form.username) }}
|
||||
{{ render_field(form.password) }}
|
||||
{{ render_field(form.remember_me) }}
|
||||
<input type=submit value=Login>
|
||||
</form>
|
||||
<p class="form-subtext">
|
||||
Don't have a login? <a href="{{ url_for('auth.signup') }}">Sign-up</a> instead!
|
||||
</p>
|
||||
{% endblock content %}
|
||||
|
||||
@@ -1,38 +1,18 @@
|
||||
{% extends 'layouts/index.html' %}
|
||||
{% from "macros.html" import render_field %}
|
||||
|
||||
{% block content %}
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<div class="notification is-danger">
|
||||
{{ messages[0] }}. Go to <a href="{{ url_for('main.login') }}">login page</a>.
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<form method="POST" action="{{ url_for('auth.signup_post') }}" class="login-form">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
Username
|
||||
<input class="input is-large" type="text" name="username" placeholder="Username" autofocus>
|
||||
</div>
|
||||
</div>
|
||||
<form method=post class="login-form">
|
||||
{{ form.csrf_token }}
|
||||
{{ render_field(form.username) }}
|
||||
{{ render_field(form.name) }}
|
||||
{{ render_field(form.password) }}
|
||||
{{ render_field(form.confirm) }}
|
||||
{{ render_field(form.accept_tos) }}
|
||||
<input type=submit value=Register>
|
||||
</form>
|
||||
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
Name
|
||||
<input class="input is-large" type="text" name="name" placeholder="Name" autofocus>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
Password
|
||||
<input class="input is-large" type="password" name="password" placeholder="Password">
|
||||
</div>
|
||||
</div>
|
||||
<button>Sign Up</button>
|
||||
</form>
|
||||
|
||||
<p class="form-subtext">
|
||||
Already have a login? <a href="{{ url_for('main.login') }}">Login</a> instead!
|
||||
</p>
|
||||
<p class="form-subtext">
|
||||
Already have a login? <a href="{{ url_for('auth.login') }}">Login</a> instead!
|
||||
</p>
|
||||
{% endblock content %}
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
<div class="user-box-top">
|
||||
|
||||
<span class="user-box-username">
|
||||
<a href="{{ url_for('main.user', username=user.username) }}">{{ user.name }}</a>
|
||||
<a href="{{ url_for('main.view_user', username=user.username) }}">{{ user.name }}</a>
|
||||
</span>
|
||||
{% with seen_text = user.get_last_seen() %}
|
||||
{% with seen_text = user.get_last_seen_text() %}
|
||||
{% if seen_text == 'Online now!' %}
|
||||
<span class="user-box-timestamp online">{{ seen_text }}</span>
|
||||
{% else %}
|
||||
@@ -24,7 +24,7 @@
|
||||
<img src="{{ url_for('static', filename='default_photo.png') }}"
|
||||
alt="{{ user.username }}'s Profile Picture">
|
||||
</div>
|
||||
<p class="user-box-minibio">{{ user.about_me }}</p>
|
||||
<p class="user-box-minibio">{{ user.display_about() }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
@@ -1,30 +1,36 @@
|
||||
{% 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 %}
|
||||
<form method="POST" action="{{ url_for('forms.new_post') }}" class="profile-form">
|
||||
<label>
|
||||
<textarea type="text" name="text" placeholder="Write a post between 15 and 1000 characters. Express yourself."></textarea>
|
||||
</label>
|
||||
<button class="button">Create Post</button>
|
||||
<form method="POST" class="form post-form">
|
||||
{{ form.csrf_token }}
|
||||
{{ render_field(form.text, show_label=False) }}
|
||||
<input type=submit value="Create Post">
|
||||
</form>
|
||||
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<div class="notification is-danger">
|
||||
{{ messages[0] }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<hr style="margin: 1.5em 0">
|
||||
{% endif %}
|
||||
|
||||
{% for post, author in posts_and_authors %}
|
||||
<div class="post-box">
|
||||
{% with post_count = posts|length %}
|
||||
<span style="display: inline-block; text-align: center; margin: 0.3em; margin-left: 1em;">Showing {{ post_count }} post{{ post_count|pluralize }}.</span>
|
||||
{% endwith %}
|
||||
|
||||
{% for post in posts %}
|
||||
<div id="post-{{ post.id }}" class="post-box">
|
||||
{% if current_user.is_authenticated %}
|
||||
<i class="fas fa-heart {% if current_user.has_liked_post(post.id) %}liked{% endif %}" onclick="like({{ post.id }})"></i>
|
||||
{% endif %}
|
||||
{{ post.text }}
|
||||
<div class="post-author no-border">
|
||||
<em>Posted by <a href="{{ url_for('main.user', username=author.username) }}">{{ author.name }}</a></em> <span
|
||||
title="{{ post.date_posted }}">{{ post.get_time_ago() }} ago</span>. |
|
||||
<a href="{{ url_for('main.view_post', post_id=post.id) }}"><span>{{ post.comments|length }} comments</span></a>
|
||||
{% with comment_count = post.comments|length %}
|
||||
<em>Posted by <a href="{{ url_for('main.view_user', username=post.author.username) }}">{{ post.author.name }}</a></em>
|
||||
<span title="{{ post.date_posted }}">{{ post.get_time_ago() }} ago</span>. |
|
||||
<a href="{{ url_for('main.view_post', post_id=post.id) }}"><span> {{ comment_count }} comment{{ comment_count|pluralize }}</span></a>
|
||||
| <span class="post-like-status">{{ post.get_like_text()|safe }}</span>
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
@@ -4,25 +4,24 @@
|
||||
{{ post.text }}
|
||||
<br>
|
||||
<div class="post-author">
|
||||
<em>Posted by <a href="{{ url_for('main.user', username=author.username) }}">{{ author.name }}</a></em> <span
|
||||
<em>Posted by <a href="{{ url_for('main.view_user', username=post.author.username) }}">{{ post.author.name }}</a></em> <span
|
||||
title="{{ post.date_posted }}">{{ post.get_time_ago() }} ago</span>.
|
||||
</div>
|
||||
<div class="post-comments">
|
||||
{% if current_user.is_authenticated %}
|
||||
<div class="post-comment add-comment">
|
||||
<form method="POST" action="{{ url_for('forms.add_comment', post_id=post.id) }}">
|
||||
<label>
|
||||
<input type="text" name="comment-text">
|
||||
</label>
|
||||
<button>Add Comment</button>
|
||||
{{ form.csrf_token }}
|
||||
{{ form.text }}<input type=submit value="Add Comment">
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% for comment, author in comments_and_authors %}
|
||||
{% for comment in post.comments %}
|
||||
<div class="post-comment">
|
||||
|
||||
<span class="comment-text">"{{ comment.text }}"</span> — <a class="comment-author"
|
||||
href="{{ url_for('main.user', username=author.username) }}">{{ author.name }}</a>
|
||||
<span class="comment-text">"{{ comment.text }}"</span> —
|
||||
<a class="comment-author"
|
||||
href="{{ url_for('main.view_user', username=comment.author) }}">{{ comment.author.name }}
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
{% block content %}
|
||||
<div class="profile-title">
|
||||
<span class="profile-username">{{ user.name }}</span>
|
||||
{% if current_user.is_admin or current_user.id == user.id %}
|
||||
<a href="{{ url_for('main.edit_user', username=current_user.username) }}"><i class="fas fa-pencil-alt fa-sm" style="padding-bottom: 3px; padding-left: 0.5em;"></i></a>
|
||||
{% if current_user.is_admin or current_user == user %}
|
||||
<a href="{{ url_for('main.edit_user', username=current_user.username) }}"><i class="fas fa-pencil-alt fa-sm"
|
||||
style="padding-bottom: 3px; padding-left: 0.5em;"></i></a>
|
||||
{% endif %}
|
||||
{% with seen_text = user.get_last_seen() %}
|
||||
{% with seen_text = user.get_last_seen_text() %}
|
||||
{% if seen_text == 'Online now!' %}
|
||||
<span class="profile-timestamp online">{{ seen_text }}</span>
|
||||
{% else %}
|
||||
@@ -20,16 +21,18 @@
|
||||
alt="{{ user.username }}'s Profile Picture">
|
||||
<div class="profile-details">
|
||||
<span title="{{ user.time_registered }}">Registered {{ user.get_registration_delta() }} ago</span><br>
|
||||
{# 0 likes<br>#}
|
||||
{% with like_count = user.get_post_likes() %}
|
||||
{{ like_count }} like{{ like_count|pluralize }}<br>
|
||||
{% endwith %}
|
||||
{% with post_count = user.get_post_count() %}
|
||||
{{ post_count }} post{% if post_count > 1 %}s{% endif %}<br>
|
||||
{{ post_count }} post{{ post_count|pluralize }}<br>
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="profile-bio">
|
||||
<p>
|
||||
<strong>About me:</strong><br>
|
||||
{{ user.about_me or "This user hasn't written a bio yet." }}
|
||||
{{ user.display_about() }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,23 +1,12 @@
|
||||
{% extends 'layouts/index.html' %}
|
||||
{% from 'macros.html' import render_field %}
|
||||
|
||||
{% block content %}
|
||||
<h3>Edit Profile</h3>
|
||||
<form method="POST" action="{{ url_for('forms.edit_profile_post', username=user.username) }}" class="profile-form">
|
||||
<label>
|
||||
Name<br>
|
||||
<input type="text" name="name" value="{{ user.name }}">
|
||||
</label><br>
|
||||
<label>
|
||||
About Me
|
||||
<textarea type="text" name="about-me">{{ user.about_me }}</textarea>
|
||||
</label>
|
||||
<button class="button">Save & Submit</button>
|
||||
<form method="POST" class="form edit-profile-form">
|
||||
{{ form.csrf_token }}
|
||||
{{ render_field(form.name) }}
|
||||
{{ render_field(form.about_me) }}
|
||||
<input type="submit" value="Save & Submit">
|
||||
</form>
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<div class="notification is-danger">
|
||||
{{ messages[0] }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% endblock %}
|
||||
|
||||
690
templates/static/license.html
Normal file
690
templates/static/license.html
Normal file
@@ -0,0 +1,690 @@
|
||||
{% extends 'layouts/index.html' %}
|
||||
|
||||
{% block content %}
|
||||
<pre>
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation,Inc.
|
||||
<https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright(C)
|
||||
<year>
|
||||
<name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C)
|
||||
<year>
|
||||
<name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
|
||||
</pre>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
4
templates/static/privacy.html
Normal file
4
templates/static/privacy.html
Normal file
@@ -0,0 +1,4 @@
|
||||
{% extends 'layouts/index.html' %}
|
||||
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
261
templates/static/tos.html
Normal file
261
templates/static/tos.html
Normal file
@@ -0,0 +1,261 @@
|
||||
{% extends 'layouts/index.html' %}
|
||||
|
||||
{% block content %}
|
||||
<p><strong>Terms of Service</strong></p>
|
||||
<p>Our Terms of Service were last updated on March 29th, 2022.</p>
|
||||
<p>Please read these terms and conditions carefully before using Our Service.</p>
|
||||
<p><strong>Interpretation and Definitions</strong></p>
|
||||
<p><strong>Interpretation</strong></p>
|
||||
<p>The words of which the initial letter is capitalized have meanings defined under the following conditions. The following definitions
|
||||
shall have the same meaning regardless of whether they appear in singular or in plural.</p>
|
||||
<p><strong>Definitions</strong></p>
|
||||
<p>For the purposes of these Terms of Service:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><strong>"Affiliate"</strong> means an entity that controls, is controlled by or is under common control with a party, where
|
||||
"control" means ownership of 50% or more of the shares, equity interest or other securities entitled to vote for election of
|
||||
directors or other managing authority.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>"Account"</strong> means a unique account created for You to access our Service or parts of our Service.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>"Company"</strong> (referred to as either "the Company", "We", "Us" or "Our" in this Agreement) refers to
|
||||
Ryan Walters & Zachary Seligman.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>"Country"</strong> refers to the United States of America.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>"Content"</strong> refers to content such as text, images, or other information that can be posted, uploaded, linked
|
||||
to or otherwise made available by You, regardless of the form of that content.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>"Device"</strong> means any device that can access the Service such as a computer, a cellphone or a digital tablet.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>"Feedback"</strong> means feedback, innovations or suggestions sent by You regarding the attributes, performance or
|
||||
features of our Service.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>"Service"</strong> refers to the Website.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>"Terms of Service"</strong> (also referred as "Terms" or "Terms of Service") mean these Terms of Service that form
|
||||
the entire agreement between You and the Company regarding the use of the Service. This Terms of Service Agreement was
|
||||
generated by <a href="https://www.termsfeed.com/blog/sample-terms-of-service-template/">TermsFeed Terms of Service
|
||||
Template</a>.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>"Third-party Social Media Service"</strong> means any services or content (including data, information, products or
|
||||
services) provided by a third-party that may be displayed, included or made available by the Service.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>"Website"</strong> refers to Runnerspace, accessible from https://runnerspace-utsa.herokuapp.com/</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>"You"</strong> means the individual accessing or using the Service, or the company, or other legal entity on behalf
|
||||
of which such individual is accessing or using the Service, as applicable.</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p><strong>Acknowledgment</strong></p>
|
||||
<p>These are the Terms of Service governing the use of this Service and the agreement that operates between You and the Company. These
|
||||
Terms of Service set out the rights and obligations of all users regarding the use of the Service.</p>
|
||||
<p>Your access to and use of the Service is conditioned on Your acceptance of and compliance with these Terms of Service. These Terms of
|
||||
Service apply to all visitors, users and others who access or use the Service.</p>
|
||||
<p>By accessing or using the Service You agree to be bound by these Terms of Service. If You disagree with any part of these Terms of
|
||||
Service then You may not access the Service.</p>
|
||||
<p>You represent that you are over the age of 18. The Company does not permit those under 18 to use the Service.</p>
|
||||
<p>Your access to and use of the Service is also conditioned on Your acceptance of and compliance with the Privacy Policy of the
|
||||
Company. Our Privacy Policy describes Our policies and procedures on the collection, use and disclosure of Your personal information
|
||||
when You use the Application or the Website and tells You about Your privacy rights and how the law protects You. Please read Our
|
||||
Privacy Policy carefully before using Our Service.</p>
|
||||
<p><strong>User Accounts</strong></p>
|
||||
<p>When You create an account with Us, You must provide Us information that is accurate, complete, and current at all times. Failure to
|
||||
do so constitutes a breach of the Terms, which may result in immediate termination of Your account on Our Service.</p>
|
||||
<p>You are responsible for safeguarding the password that You use to access the Service and for any activities or actions under Your
|
||||
password, whether Your password is with Our Service or a Third-Party Social Media Service.</p>
|
||||
<p>You agree not to disclose Your password to any third party. You must notify Us immediately upon becoming aware of any breach of
|
||||
security or unauthorized use of Your account.</p>
|
||||
<p>You may not use as a username the name of another person or entity or that is not lawfully available for use, a name or trademark
|
||||
that is subject to any rights of another person or entity other than You without appropriate authorization, or a name that is
|
||||
otherwise offensive, vulgar or obscene.</p>
|
||||
<p><strong>Content</strong></p>
|
||||
<p><strong>Your Right to Post Content</strong></p>
|
||||
<p>Our Service allows You to post Content. You are responsible for the Content that You post to the Service, including its legality,
|
||||
reliability, and appropriateness.</p>
|
||||
<p>By posting Content to the Service, You grant Us the right and license to use, modify, publicly perform, publicly display, reproduce,
|
||||
and distribute such Content on and through the Service. You retain any and all of Your rights to any Content You submit, post or
|
||||
display on or through the Service and You are responsible for protecting those rights. You agree that this license includes the
|
||||
right for Us to make Your Content available to other users of the Service, who may also use Your Content subject to these Terms.</p>
|
||||
<p>You represent and warrant that: (i) the Content is Yours (You own it) or You have the right to use it and grant Us the rights and
|
||||
license as provided in these Terms, and (ii) the posting of Your Content on or through the Service does not violate the privacy
|
||||
rights, publicity rights, copyrights, contract rights or any other rights of any person.</p>
|
||||
<p><strong>Content Restrictions</strong></p>
|
||||
<p>The Company is not responsible for the content of the Service's users. You expressly understand and agree that You are solely
|
||||
responsible for the Content and for all activity that occurs under your account, whether done so by You or any third person using
|
||||
Your account.</p>
|
||||
<p>You may not transmit any Content that is unlawful, offensive, upsetting, intended to disgust, threatening, libelous, defamatory,
|
||||
obscene or otherwise objectionable. Examples of such objectionable Content include, but are not limited to, the following:</p>
|
||||
<ul>
|
||||
<li>Unlawful or promoting unlawful activity.</li>
|
||||
<li>Defamatory, discriminatory, or mean-spirited content, including references or commentary about religion, race, sexual
|
||||
orientation, gender, national/ethnic origin, or other targeted groups.
|
||||
</li>
|
||||
<li>Spam, machine – or randomly – generated, constituting unauthorized or unsolicited advertising, chain letters, any other form of
|
||||
unauthorized solicitation, or any form of lottery or gambling.
|
||||
</li>
|
||||
<li>Containing or installing any viruses, worms, malware, trojan horses, or other content that is designed or intended to disrupt,
|
||||
damage, or limit the functioning of any software, hardware or telecommunications equipment or to damage or obtain unauthorized
|
||||
access to any data or other information of a third person.
|
||||
</li>
|
||||
<li>Infringing on any proprietary rights of any party, including patent, trademark, trade secret, copyright, right of publicity or
|
||||
other rights.
|
||||
</li>
|
||||
<li>Impersonating any person or entity including the Company and its employees or representatives.</li>
|
||||
<li>Violating the privacy of any third person.</li>
|
||||
<li>False information and features.</li>
|
||||
</ul>
|
||||
<p>The Company reserves the right, but not the obligation, to, in its sole discretion, determine whether or not any Content is
|
||||
appropriate and complies with this Terms, refuse or remove this Content. The Company further reserves the right to make formatting
|
||||
and edits and change the manner of any Content. The Company can also limit or revoke the use of the Service if You post such
|
||||
objectionable Content.<br/>
|
||||
As the Company cannot control all content posted by users and/or third parties on the Service, you agree to use the Service at your
|
||||
own risk. You understand that by using the Service You may be exposed to content that You may find offensive, indecent, incorrect or
|
||||
objectionable, and You agree that under no circumstances will the Company be liable in any way for any content, including any errors
|
||||
or omissions in any content, or any loss or damage of any kind incurred as a result of your use of any content.</p>
|
||||
<p><strong>Content Backups</strong></p>
|
||||
<p>Although regular backups of Content are performed, the Company does not guarantee there will be no loss or corruption of data.</p>
|
||||
<p>Corrupt or invalid backup points may be caused by, without limitation, Content that is corrupted prior to being backed up or that
|
||||
changes during the time a backup is performed.</p>
|
||||
<p>The Company will provide support and attempt to troubleshoot any known or discovered issues that may affect the backups of Content.
|
||||
But You acknowledge that the Company has no liability related to the integrity of Content or the failure to successfully restore
|
||||
Content to a usable state.</p>
|
||||
<p>You agree to maintain a complete and accurate copy of any Content in a location independent of the Service.</p>
|
||||
<p><strong>Copyright Policy</strong></p>
|
||||
<p><strong>Intellectual Property Infringement</strong></p>
|
||||
<p>We respect the intellectual property rights of others. It is Our policy to respond to any claim that Content posted on the Service
|
||||
infringes a copyright or other intellectual property infringement of any person.</p>
|
||||
<p>If You are a copyright owner, or authorized on behalf of one, and You believe that the copyrighted work has been copied in a way that
|
||||
constitutes copyright infringement that is taking place through the Service, You must submit Your notice in writing to the attention
|
||||
of our copyright agent via email ([ryanchwalters@gmail.com]) and include in Your notice a detailed description of the
|
||||
alleged infringement.</p>
|
||||
<p>You may be held accountable for damages (including costs and attorneys' fees) for misrepresenting that any Content is infringing Your
|
||||
copyright.</p>
|
||||
<p><strong>DMCA Notice and DMCA Procedure for Copyright Infringement Claims</strong></p>
|
||||
<p>You may submit a notification pursuant to the Digital Millennium Copyright Act (DMCA) by providing our Copyright Agent with the
|
||||
following information in writing (see 17 U.S.C 512(c)(3) for further detail):</p>
|
||||
<ul>
|
||||
<li>An electronic or physical signature of the person authorized to act on behalf of the owner of the copyright's interest.</li>
|
||||
<li>A description of the copyrighted work that You claim has been infringed, including the URL (i.e., web page address) of the
|
||||
location where the copyrighted work exists or a copy of the copyrighted work.
|
||||
</li>
|
||||
<li>Identification of the URL or other specific location on the Service where the material that You claim is infringing is
|
||||
located.
|
||||
</li>
|
||||
<li>Your address, telephone number, and email address.</li>
|
||||
<li>A statement by You that You have a good faith belief that the disputed use is not authorized by the copyright owner, its agent,
|
||||
or the law.
|
||||
</li>
|
||||
<li>A statement by You, made under penalty of perjury, that the above information in Your notice is accurate and that You are the
|
||||
copyright owner or authorized to act on the copyright owner's behalf.
|
||||
</li>
|
||||
</ul>
|
||||
<p>You can contact our copyright agent via email ([ryanchwalters@gmail.com]). Upon receipt of a notification, the Company
|
||||
will take whatever action, in its sole discretion, it deems appropriate, including removal of the challenged content from the
|
||||
Service.</p>
|
||||
<p><strong>Intellectual Property</strong></p>
|
||||
<p>The Service and its original content (excluding Content provided by You or other users), features and functionality are and will
|
||||
remain the exclusive property of the Company and its licensors.</p>
|
||||
<p>The Service is protected by copyright, trademark, and other laws of both the Country and foreign countries.</p>
|
||||
<p>Our trademarks and trade dress may not be used in connection with any product or service without the prior written consent of the
|
||||
Company.</p>
|
||||
<p><strong>Your Feedback to Us</strong></p>
|
||||
<p>You assign all rights, title and interest in any Feedback You provide the Company. If for any reason such assignment is ineffective,
|
||||
You agree to grant the Company a non-exclusive, perpetual, irrevocable, royalty free, worldwide right and license to use, reproduce,
|
||||
disclose, sub-license, distribute, modify and exploit such Feedback without restriction.</p>
|
||||
<p><strong>Links to Other Websites</strong></p>
|
||||
<p>Our Service may contain links to third-party web sites or services that are not owned or controlled by the Company.</p>
|
||||
<p>The Company has no control over, and assumes no responsibility for, the content, privacy policies, or practices of any third party
|
||||
web sites or services. You further acknowledge and agree that the Company shall not be responsible or liable, directly or
|
||||
indirectly, for any damage or loss caused or alleged to be caused by or in connection with the use of or reliance on any such
|
||||
content, goods or services available on or through any such web sites or services.</p>
|
||||
<p>We strongly advise You to read the terms and conditions and privacy policies of any third-party web sites or services that You
|
||||
visit.</p>
|
||||
<p><strong>Termination</strong></p>
|
||||
<p>We may terminate or suspend Your Account immediately, without prior notice or liability, for any reason whatsoever, including without
|
||||
limitation if You breach these Terms of Service.</p>
|
||||
<p>Upon termination, Your right to use the Service will cease immediately. If You wish to terminate Your Account, You may simply
|
||||
discontinue using the Service.</p>
|
||||
<p><strong>Limitation of Liability</strong></p>
|
||||
<p>Notwithstanding any damages that You might incur, the entire liability of the Company and any of its suppliers under any provision of
|
||||
this Terms and Your exclusive remedy for all of the foregoing shall be limited to the amount actually paid by You through the
|
||||
Service or 100 USD if You haven't purchased anything through the Service.</p>
|
||||
<p>To the maximum extent permitted by applicable law, in no event shall the Company or its suppliers be liable for any special,
|
||||
incidental, indirect, or consequential damages whatsoever (including, but not limited to, damages for loss of profits, loss of data
|
||||
or other information, for business interruption, for personal injury, loss of privacy arising out of or in any way related to the
|
||||
use of or inability to use the Service, third-party software and/or third-party hardware used with the Service, or otherwise in
|
||||
connection with any provision of this Terms), even if the Company or any supplier has been advised of the possibility of such
|
||||
damages and even if the remedy fails of its essential purpose.</p>
|
||||
<p>Some states do not allow the exclusion of implied warranties or limitation of liability for incidental or consequential damages,
|
||||
which means that some of the above limitations may not apply. In these states, each party's liability will be limited to the
|
||||
greatest extent permitted by law.</p>
|
||||
<p><strong>"AS IS" and "AS AVAILABLE" Disclaimer</strong></p>
|
||||
<p>The Service is provided to You "AS IS" and "AS AVAILABLE" and with all faults and defects without warranty of any kind. To the
|
||||
maximum extent permitted under applicable law, the Company, on its own behalf and on behalf of its Affiliates and its and their
|
||||
respective licensors and service providers, expressly disclaims all warranties, whether express, implied, statutory or otherwise,
|
||||
with respect to the Service, including all implied warranties of merchantability, fitness for a particular purpose, title and
|
||||
non-infringement, and warranties that may arise out of course of dealing, course of performance, usage or trade practice. Without
|
||||
limitation to the foregoing, the Company provides no warranty or undertaking, and makes no representation of any kind that the
|
||||
Service will meet Your requirements, achieve any intended results, be compatible or work with any other software, applications,
|
||||
systems or services, operate without interruption, meet any performance or reliability standards or be error free or that any errors
|
||||
or defects can or will be corrected.</p>
|
||||
<p>Without limiting the foregoing, neither the Company nor any of the company's provider makes any representation or warranty of any
|
||||
kind, express or implied: (i) as to the operation or availability of the Service, or the information, content, and materials or
|
||||
products included thereon; (ii) that the Service will be uninterrupted or error-free; (iii) as to the accuracy, reliability, or
|
||||
currency of any information or content provided through the Service; or (iv) that the Service, its servers, the content, or e-mails
|
||||
sent from or on behalf of the Company are free of viruses, scripts, trojan horses, worms, malware, timebombs or other harmful
|
||||
components.</p>
|
||||
<p>Some jurisdictions do not allow the exclusion of certain types of warranties or limitations on applicable statutory rights of a
|
||||
consumer, so some or all of the above exclusions and limitations may not apply to You. But in such a case the exclusions and
|
||||
limitations set forth in this section shall be applied to the greatest extent enforceable under applicable law.</p>
|
||||
<p><strong>Governing Law</strong></p>
|
||||
<p>The laws of the Country, excluding its conflicts of law rules, shall govern this Terms and Your use of the Service. Your use of the
|
||||
Application may also be subject to other local, state, national, or international laws.</p>
|
||||
<p><strong>Disputes Resolution</strong></p>
|
||||
<p>If You have any concern or dispute about the Service, You agree to first try to resolve the dispute informally by contacting the
|
||||
Company.</p>
|
||||
<p><strong>For European Union (EU) Users</strong></p>
|
||||
<p>If You are a European Union consumer, you will benefit from any mandatory provisions of the law of the country in which you are
|
||||
resident in.</p>
|
||||
<p><strong>United States Legal Compliance</strong></p>
|
||||
<p>You represent and warrant that (i) You are not located in a country that is subject to the United States government embargo, or that
|
||||
has been designated by the United States government as a "terrorist supporting" country, and (ii) You are not listed on any United
|
||||
States government list of prohibited or restricted parties.</p>
|
||||
<p><strong>Severability and Waiver</strong></p>
|
||||
<p><strong>Severability</strong></p>
|
||||
<p>If any provision of these Terms is held to be unenforceable or invalid, such provision will be changed and interpreted to accomplish
|
||||
the objectives of such provision to the greatest extent possible under applicable law and the remaining provisions will continue in
|
||||
full force and effect.</p>
|
||||
<p><strong>Waiver</strong></p>
|
||||
<p>Except as provided herein, the failure to exercise a right or to require performance of an obligation under these Terms shall not
|
||||
effect a party's ability to exercise such right or require such performance at any time thereafter nor shall be the waiver of a
|
||||
breach constitute a waiver of any subsequent breach.</p>
|
||||
<p><strong>Translation Interpretation</strong></p>
|
||||
<p>These Terms of Service may have been translated if We have made them available to You on our Service.<br/>
|
||||
You agree that the original English text shall prevail in the case of a dispute.</p>
|
||||
<p><strong>Changes to These Terms of Service</strong></p>
|
||||
<p>We reserve the right, at Our sole discretion, to modify or replace these Terms at any time. If a revision is material We will make
|
||||
reasonable efforts to provide at least 30 days' notice prior to any new terms taking effect. What constitutes a material change will
|
||||
be determined at Our sole discretion.</p>
|
||||
<p>By continuing to access or use Our Service after those revisions become effective, You agree to be bound by the revised terms. If You
|
||||
do not agree to the new terms, in whole or in part, please stop using the website and the Service.</p>
|
||||
<p><strong>Contact Us</strong></p>
|
||||
<p>If you have any questions about these Terms of Service, You can contact us:</p>
|
||||
<ul>
|
||||
<li>By sending us an email: [ryanchwalters@gmail.com]</li>
|
||||
</ul>
|
||||
<p></p>
|
||||
|
||||
{% endblock %}
|
||||
16
validators.py
Normal file
16
validators.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from typing import Optional
|
||||
from profanity_filter import ProfanityFilter
|
||||
from wtforms.validators import ValidationError
|
||||
|
||||
pf = ProfanityFilter()
|
||||
|
||||
|
||||
class NoProfanity(object):
|
||||
def __init__(self, message: Optional[str] = None):
|
||||
if not message:
|
||||
message = 'Profanity is not acceptable on Runnerspace'
|
||||
self.message = message
|
||||
|
||||
def __call__(self, form, field):
|
||||
if pf.is_profane(field.data):
|
||||
raise ValidationError(self.message)
|
||||
Reference in New Issue
Block a user