Compare commits
691 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 17034c928b | |||
| 395041fd44 | |||
| c117c7e641 | |||
| 01fc8537cf | |||
| 12ad9759cf | |||
| 057639be7c | |||
| 87e7be1c10 | |||
| 1db77346d0 | |||
| 1e6ac0fcd9 | |||
| b3c74c2172 | |||
| 0805958033 | |||
| 5c0e978c93 | |||
| 520298bae4 | |||
| 969adc4836 | |||
| d9eca06197 | |||
| 48f2e90e49 | |||
| 358247a2a1 | |||
| de4d2fe5c0 | |||
| e1102eb7db | |||
| a8df9590fd | |||
| 3e453e22ec | |||
| beeab0ec90 | |||
| 9353a7eaaa | |||
| cac8f3046d | |||
| 66eeac372c | |||
| 4a6a3cf708 | |||
| cb44c3f5a8 | |||
| eb0a34e959 | |||
| 8d683cc1a0 | |||
| ee41dd4cfe | |||
| 3d50e6e3b3 | |||
| 4f8d11881d | |||
| 5c924d3277 | |||
| 7f703a8386 | |||
| f063ea4741 | |||
| ca643dd298 | |||
| e19225e49e | |||
| 9ffbca679e | |||
| d90a10e462 | |||
| b6f25d041b | |||
| 4321c4bee5 | |||
| d1b3641754 | |||
| 5281c1bc7d | |||
| aae78bf479 | |||
| af17764346 | |||
| a8d321609d | |||
| 23ccbe2d02 | |||
| 47239016c5 | |||
| f4f6960b9d | |||
| c4c299eed6 | |||
| e20b59b43d | |||
| 901232b683 | |||
| 40c5fa69f5 | |||
| 7694396454 | |||
| dafc149bba | |||
| f23ec27247 | |||
| efb179dec2 | |||
| 86222aa92e | |||
| a5fb5cd360 | |||
| 6f6e53a483 | |||
| f13a2c6f1e | |||
| 59012cacfb | |||
| 21c5471d73 | |||
| e1ee70b46f | |||
| eb56b8d6a0 | |||
| d597046f41 | |||
| ae1c873536 | |||
| 1f7a2a77f6 | |||
| 9269465f6b | |||
| 883adfd242 | |||
| 1e890f9a28 | |||
| 2bb13d2e8c | |||
| cb02e05d55 | |||
| 05777f6109 | |||
| f3f042b3b9 | |||
| dfb13b1f96 | |||
| 6fa5038589 | |||
| 55f53e3346 | |||
| f36037d36d | |||
| 500bca80c1 | |||
| fc0361ed3a | |||
| df1485ce49 | |||
| b8bceb31c6 | |||
| f0308c3505 | |||
| e7ece1ed13 | |||
| 8ed7f7ec8f | |||
| feaef4a624 | |||
| 7d9aae2a59 | |||
| a88f8ca638 | |||
| be6a5f9add | |||
| ed4498578d | |||
| 118be50b3c | |||
| 1e2184ae20 | |||
| 8c1e1a64bf | |||
| b9c28968fe | |||
| 703d8d7519 | |||
| 8e52d99ba4 | |||
| d4020671c1 | |||
| 5c432e5801 | |||
| aac13297de | |||
| f60a4a4f60 | |||
| 0ed4de74b0 | |||
| 48bd26342a | |||
| b8e884a5aa | |||
| 1dbef5ca4b | |||
| e43ae7f96a | |||
| eab256d17f | |||
| bbdcbbf0ce | |||
| ec9bc3fe1f | |||
| 80f92a384e | |||
| 95251d23ad | |||
| c16bf574ce | |||
| 0e44d92baa | |||
| f9c1c67a38 | |||
| 286e2b6237 | |||
| fb78794261 | |||
| 371a65d361 | |||
| 7bd3c8dfd1 | |||
| e5aa6ea25d | |||
| de9afd3275 | |||
| 79e9f17f70 | |||
| 12cc3c5d5b | |||
| 404cea74ff | |||
| 16e94947f2 | |||
| aff0b0560f | |||
| de19a6fc65 | |||
| 1f669aadfc | |||
| 33f428559c | |||
| 0c3b9eb250 | |||
| c654fab94a | |||
| f2ef3c08a7 | |||
| 8a66e8af3f | |||
| 41d1cc6013 | |||
| c9c6a4ae81 | |||
| a5131e33dc | |||
| a19d1db990 | |||
| b6e7da6808 | |||
| f21fab937f | |||
| 30fccfe69b | |||
| b692d76f7d | |||
| 56533d4a16 | |||
| 8a22b475f9 | |||
| ae3a1be5e1 | |||
| 8199afc88f | |||
| 2d7ef8fc1d | |||
| d75d78ccb3 | |||
| 42489f50b6 | |||
| 52fde19380 | |||
| b1fb3e5baa | |||
| 5f3dae64c1 | |||
| b31ce66ef9 | |||
| 93bd561ff9 | |||
| d0d93be96b | |||
| 327a05cf63 | |||
| 1394467abe | |||
| ace73f15ef | |||
| a5e1fb4c38 | |||
| ac2451fe64 | |||
| 5f9789a884 | |||
| e0fbd220f0 | |||
| a0932fed04 | |||
| 42aa908f1c | |||
| da9dc0e8fb | |||
| 36f6396148 | |||
| 12facddcd5 | |||
| 429ecb4e0e | |||
| 8019d9c99a | |||
| 9f9c92385d | |||
| 9ecfc3b6f8 | |||
| d00f2d9d53 | |||
| 37a032aa9f | |||
| fd8e13eaa9 | |||
| b5d1e08d22 | |||
| 23321f90c2 | |||
| 90c6f67c24 | |||
| 7e2b29bfb0 | |||
| cd6aa80f59 | |||
| d5fdc9383f | |||
| 53865efc78 | |||
| 8cbd442371 | |||
| f54cf58dc2 | |||
| ede8e1cb21 | |||
| aaff15ee8c | |||
| bb7c85d770 | |||
| b7160f4df6 | |||
| a6b57bc352 | |||
| 70f7e05fe1 | |||
| 15f17b7af4 | |||
| a5650f2720 | |||
| 4758366063 | |||
| e7a5c2b352 | |||
| 3c14df129d | |||
| 5660f8d59d | |||
| 36ec2a539c | |||
| 339e51cca7 | |||
| 0726ba214c | |||
| 0e032ae029 | |||
| b9dead1c8b | |||
| 9010b68a39 | |||
| 252c14a377 | |||
| 4abfe801ec | |||
| c23a10eb6e | |||
| 11b55008ea | |||
| 46dcc16c59 | |||
| d42dff1165 | |||
| 11da418f60 | |||
| 981e78ff7e | |||
| c798bbd230 | |||
| e22d97e15a | |||
| c662369259 | |||
| 11b7be4ca0 | |||
| a029314bc9 | |||
| 8dd2d4f4ea | |||
| a4efbd4f0b | |||
| f63b8c6506 | |||
| 198fefbdf5 | |||
| 135bd7b9cd | |||
| 4cd0383322 | |||
| caa9a9ff8f | |||
| 524dc2a399 | |||
| 793d9632b2 | |||
| 5892a875c8 | |||
| b130bf9e19 | |||
| 1314362df9 | |||
| 5a1c032f1b | |||
| fae4bc86ad | |||
| 664092d52f | |||
| 22b1dd827d | |||
| 50df844e23 | |||
| 45b6b41d60 | |||
| a0a79e0191 | |||
| 46def40a14 | |||
| a76113dcaf | |||
| f56a04c3ab | |||
| 7f891b6ebf | |||
| 1343496a12 | |||
| 1735dd28a2 | |||
| bfc9ee4d01 | |||
| 9321d09489 | |||
| 7269ea03db | |||
| 2a3a15c92d | |||
| a7f1729e6e | |||
| cdccb0bfe8 | |||
| 48501a9069 | |||
| a175765e33 | |||
| 3d57c2e482 | |||
| bac4334fb8 | |||
| aa3e7c2f36 | |||
| 46cd524f70 | |||
| 86fb37abc4 | |||
| 9349b91926 | |||
| b9c86e2074 | |||
| 68672474e4 | |||
| a08197cc10 | |||
| e28620d9fb | |||
| ded98ed757 | |||
| 18fe69cbcd | |||
| e35d07ebc3 | |||
| e772a8b57f | |||
| 9c3ebc9877 | |||
| 3d13c3b8c8 | |||
| 6c3e788f13 | |||
| aceac0bf8b | |||
| 78d04f1715 | |||
| 1d773ea4e6 | |||
| a595567db1 | |||
| 945d3e1c1f | |||
| d58ea953af | |||
| b9475a045f | |||
| 32c2a1a3d8 | |||
| fc93f2b3f0 | |||
| f33778f97a | |||
| 1c72a03f26 | |||
| 3dcbdbf23c | |||
| 8143d501fa | |||
| 3471eedf4a | |||
| b6a3dad2ab | |||
| 375e6d9625 | |||
| 4d144ec704 | |||
| 92574a0f76 | |||
| ade73f3105 | |||
| ee75620cd8 | |||
| 94486e2b0a | |||
| a26e985edf | |||
| cf9ac795eb | |||
| 370c047c16 | |||
| a562a67923 | |||
| 078dbd8901 | |||
| 64d624a6df | |||
| 90ab57e4cb | |||
| a00ddf18e6 | |||
| 5048dc2466 | |||
| 7209fe3e20 | |||
| 4e35e67ef1 | |||
| 830b57d245 | |||
| 06930644a6 | |||
| a519387773 | |||
| ed40596389 | |||
| 4c855c05ed | |||
| e7189245d5 | |||
| 709a4ba83c | |||
| fb54ba2462 | |||
| c6b55030cf | |||
| af04557f58 | |||
| 2c100bddf2 | |||
| 8e3642c668 | |||
| 87d78b749b | |||
| 604dbeca93 | |||
| 8e9e7d65d0 | |||
| e2c317e507 | |||
| 5a7fa35465 | |||
| 2601a1c1ca | |||
| 0e26679f24 | |||
| d5ab659217 | |||
| 2d5919d572 | |||
| f56a074a3c | |||
| 24dc432f01 | |||
| 016193de07 | |||
| f04d8fecd3 | |||
| 36276c7c88 | |||
| 4a2cf0b55b | |||
| 400436a73f | |||
| 4c378dfba6 | |||
| c3a9de71f4 | |||
| ecf2e9ef57 | |||
| 45ac70eba2 | |||
| 3b66c6aa3a | |||
| e37b882481 | |||
| 5c7a47e002 | |||
| bbaa73dc1a | |||
| 9f5806f019 | |||
| 6de86c3143 | |||
| c9c0de6c3c | |||
| 4f827ed3a8 | |||
| aedac79743 | |||
| 84a39bf0d4 | |||
| 75c6d099f5 | |||
| 319957c01e | |||
| 26dc33ea2f | |||
| 7597eddd7c | |||
| 0441b4e56f | |||
| cbff21136c | |||
| 03f0d19b01 | |||
| ca65c6c6e1 | |||
| 2b095f16bf | |||
| 930bf99fa1 | |||
| 366527e8e9 | |||
| 4e06240307 | |||
| 38608a5278 | |||
| b19f30cca4 | |||
| b206e809ac | |||
| e88f741fa7 | |||
| 6958e71bfb | |||
| c6ad0b2a59 | |||
| da9ea4f413 | |||
| 5703c0a6dd | |||
| b70943dd35 | |||
| 0f7099be81 | |||
| 9cbc1637b4 | |||
| d01be7d84c | |||
| 28511e0cc4 | |||
| 3774a2caee | |||
| 70277d3712 | |||
| 18cb092d06 | |||
| 417f34e35f | |||
| c62ff5b2c0 | |||
| ad422a134d | |||
| 9fce9ec8dd | |||
| a329137de2 | |||
| b328c2613c | |||
| 62a7b6773c | |||
| 625dd35b9e | |||
| f99b7bc8ab | |||
| e779162ec2 | |||
| 17345345b5 | |||
| 3df526d5b5 | |||
| d5f30176c4 | |||
| 308e5af6ff | |||
| 268fe6a6b9 | |||
| a24914126f | |||
| 1ca27bc072 | |||
| c96d93c2ce | |||
| cd38af22db | |||
| c440c25e41 | |||
| e105ab61b7 | |||
| 10b058afa0 | |||
| d18f33a87a | |||
| 62190fa821 | |||
| a6210c9d6e | |||
| fdece531af | |||
| 8073235466 | |||
| 7dc3c3b988 | |||
| 21793dffd7 | |||
| ca337c4fac | |||
| 76beb8d62e | |||
| c5bfd02f09 | |||
| 9f4ca058bd | |||
| 603c208ae4 | |||
| 754777d69e | |||
| 829a0e49d8 | |||
| f12babf2b1 | |||
| d6249169c6 | |||
| 6aa1cd9a63 | |||
| 09795c30dd | |||
| 59268dc102 | |||
| 979378d142 | |||
| 6ce7f0ef22 | |||
| 71a8dd5f17 | |||
| e86fef1766 | |||
| 3cb63f791a | |||
| f96415bfe6 | |||
| 365f70af98 | |||
| c1ddbfdf48 | |||
| e575e3dfa8 | |||
| 560f53cfb6 | |||
| d8c350c20f | |||
| 5b718f24ed | |||
| 6634bee77b | |||
| 2a3e73d409 | |||
| 21bdb0d66d | |||
| 5db4ad3e63 | |||
| 2ce13cef72 | |||
| 02551049e0 | |||
| 55e9fa6a20 | |||
| c82378baa8 | |||
| 541d5b4c5e | |||
| f9ab3f8db2 | |||
| 6c5047be30 | |||
| 25f6d2bf4a | |||
| 2cd68873de | |||
| d987ca271f | |||
| 0a37dd4b8b | |||
| b4ca25db3d | |||
| 738b36c406 | |||
| c785363384 | |||
| 32cb8a9d9e | |||
| 29e23a8d3b | |||
| 1a6403963a | |||
| e096774233 | |||
| 222759a1bc | |||
| 7a3043f644 | |||
| f1986bc3f7 | |||
| 75d2f17a3f | |||
| 317f4c0ce9 | |||
| 4a7bfa9567 | |||
| fec3cbb550 | |||
| 497ce5bdb4 | |||
| 53b593b380 | |||
| 2d03b1f051 | |||
| d3c190096a | |||
| 2998a7bf22 | |||
| 43bd452a30 | |||
| dfdcc99111 | |||
| ea409dae07 | |||
| 2810bfdce2 | |||
| ff23e6c9a2 | |||
| 88c369de32 | |||
| 52a34cd9b4 | |||
| 46eb99b534 | |||
| 0eba5f875a | |||
| b93692c160 | |||
| 00f9f9cd91 | |||
| 2c0d52a3d5 | |||
| 4a67932002 | |||
| 339e3025b8 | |||
| 4d88605192 | |||
| bd3d81e80c | |||
| 98683197e0 | |||
| 2ded6300ab | |||
| 79efee0979 | |||
| 9db31d4b8c | |||
| d7c7ef1041 | |||
| 483dd4182e | |||
| f63a2be01a | |||
| 838b13e39f | |||
| 7b4abe32a7 | |||
| 26889a8b59 | |||
| 60302e02b6 | |||
| 72023a6634 | |||
| dd1194a1c7 | |||
| d5fbfc950f | |||
| d4fa588c8f | |||
| 2a5ff14cb5 | |||
| df17478d72 | |||
| 38fba55798 | |||
| 5b3e7bf426 | |||
| cb07a4c80f | |||
| 85512e61a2 | |||
| 380736ce26 | |||
| 9eb6feb589 | |||
| 23eee02208 | |||
| ca4dfd03e4 | |||
| 5c613a82c0 | |||
| 34ffad38a6 | |||
| b131523bf3 | |||
| db2bd7e195 | |||
| 51ccad038d | |||
| 18810dcfe2 | |||
| ed1815b78e | |||
| 246a2a856c | |||
| 77b3916985 | |||
| 298a9b406a | |||
| e5d6291aac | |||
| 667d009cf8 | |||
| 3edc689e5f | |||
| 938cca55fe | |||
| d5e177654f | |||
| 59bbbc6bf4 | |||
| ec814c9026 | |||
| b390437790 | |||
| d08e56bb5a | |||
| ccff6ab4cb | |||
| 04c1cb00c5 | |||
| 1f2be8b8ee | |||
| f2c0b4e4b5 | |||
| 77fd9af112 | |||
| 7ea3ce66ce | |||
| a91869bf47 | |||
| 9ae1cd4c34 | |||
| 475fb00001 | |||
| 168cf68a70 | |||
| 36d4ac0681 | |||
| 1afc7b480a | |||
| b88046e228 | |||
| 4f00859d1b | |||
| 1b68f037ae | |||
| 7067303758 | |||
| e499ffba22 | |||
| c4e7ca3ac2 | |||
| 55367963fb | |||
| 5522f4fc04 | |||
| 332507d433 | |||
| fe703d4e89 | |||
| ef6d6d9a91 | |||
| 128b191af5 | |||
| 5612bfe0b7 | |||
| d0f9a97394 | |||
| 67ecf6e870 | |||
| fd27577407 | |||
| 83ccf84da3 | |||
| 968534d28e | |||
| cd60423f1a | |||
| 9bb2587c49 | |||
| ef94a5873c | |||
| 8dbbe9eaf9 | |||
| 3afac85222 | |||
| 9eae4985d3 | |||
| 63c7bb8848 | |||
| 28ef21aa52 | |||
| 6a6ab16e09 | |||
| 5130a638b9 | |||
| bc9b5effb2 | |||
| 5ee412b050 | |||
| 65412e6df9 | |||
| 22ca680f3c | |||
| 83268d3a67 | |||
| d24bd0aa8b | |||
| c52834aa0d | |||
| 306b9e8152 | |||
| 30bb0d4657 | |||
| fdd7408f53 | |||
| 7f77297100 | |||
| e8693f282f | |||
| 8477462e07 | |||
| c28268faf3 | |||
| 24a759c61a | |||
| 0d0a7dba90 | |||
| 6de87c9e6c | |||
| 104f265e9b | |||
| f21d819c77 | |||
| ac1828b5f9 | |||
| 22bb2de043 | |||
| 664ad37181 | |||
| c2c98d12bc | |||
| cdc8b85f3c | |||
| c0d9949037 | |||
| 5dbe7a99b9 | |||
| 086596f68c | |||
| 7a206674ab | |||
| 9698512ccf | |||
| f4d144980d | |||
| 1513cbda7f | |||
| 1becb82b1b | |||
| 91880b163d | |||
| 81dd452f81 | |||
| 24642ad83e | |||
| 9b8f4036a9 | |||
| 86395f0727 | |||
| e5b617dcba | |||
| 581c012a72 | |||
| 4d8f338649 | |||
| e5eaaf857e | |||
| e0835b70df | |||
| ed471cdf94 | |||
| f825537b52 | |||
| 485e08e6c6 | |||
| 7ff02a6a94 | |||
| ec0148bb86 | |||
| 9aa0793ab4 | |||
| 54220682cf | |||
| b13cff14dc | |||
| f86bdfd2ea | |||
| 3933b9b55c | |||
| 7a784c8243 | |||
| 10ddf8bf7f | |||
| 0779a2c7d4 | |||
| 053ae0b52f | |||
| be8b6372db | |||
| 6068417fc5 | |||
| 14d0b23d06 | |||
| 1f6ddcd1fd | |||
| 701fda5ab5 | |||
| 47731a8d46 | |||
| 0ade795415 | |||
| 2a9a60ae84 | |||
| efdc9b4175 | |||
| 6076903b42 | |||
| d43218fdb2 | |||
| 554dbd677a | |||
| 6a7e961cf6 | |||
| e49e614958 | |||
| 70fe6ff807 | |||
| f6932afcb8 | |||
| e3901265d8 | |||
| 368a4948ec | |||
| 6c07502866 | |||
| c1c8bc47d7 | |||
| 8466a857c3 | |||
| 68d9224ea3 | |||
| aac26b0e50 | |||
| a1a9fa9d3e | |||
| 5625326f38 | |||
| 8841cffb25 | |||
| d72bf8c3e2 | |||
| 04195f5d92 | |||
| 24a3ad0639 | |||
| 72097f891d | |||
| 0686e98384 | |||
| 7d9bef932f | |||
| e69cb50a9c | |||
| 890187cff2 | |||
| 46a307a5b0 | |||
| 7706c7dfbf | |||
| a946cee891 | |||
| f2a9d8d1b3 | |||
| e128a31a34 | |||
| d7acca36fd | |||
| d28560b77e | |||
| ee6dd0ddae | |||
| feb64eb769 | |||
| ecc76e453e | |||
| 949ca542ab | |||
| b090195d9f | |||
| 1f1e7dc814 | |||
| 3606a750be | |||
| 8762bc1b07 | |||
| c7f279b7e9 | |||
| 92f1f077d2 | |||
| 50120f974d | |||
| 713a258d48 | |||
| 0a7e6df734 | |||
| 8c769ba280 | |||
| 5bcb64f7f8 | |||
| 944f40d89f | |||
| b15421efee | |||
| 3ddd66dcbb | |||
| 8af0367edf | |||
| b95fc1adbe | |||
| 63f400995b | |||
| 8aa657ed99 | |||
| 627c72bb7f | |||
| a8a75d90ac | |||
| 0b1b704115 | |||
| 0babc42359 | |||
| 4a80d42dda | |||
| a2daded6b3 | |||
| 2044853f94 | |||
| 9a3e4cc7dc | |||
| f508a222c6 | |||
| 193e526e67 | |||
| f77643af44 | |||
| 9e900a3436 | |||
| 75988ffe71 | |||
| cb1f3f0d07 | |||
| 4d8017aa2b | |||
| 365f37dcea | |||
| 2472143146 | |||
| a1184299b1 | |||
| 4d4b8aeb99 | |||
| c41302d046 | |||
| aaefd2917e |
Generated
+18
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="dataSourceStorageLocal" created-in="IU-261.25134.95">
|
||||||
|
<data-source name="articles" uuid="315cb5c9-2b0f-435b-b602-59823b160908">
|
||||||
|
<database-info product="SQLite" version="3.51.1" jdbc-version="4.2" driver-name="SQLite JDBC" driver-version="3.51.1.0" dbms="SQLITE" exact-version="3.51.1" exact-driver-version="3.51">
|
||||||
|
<identifier-quote-string>"</identifier-quote-string>
|
||||||
|
</database-info>
|
||||||
|
<case-sensitivity plain-identifiers="mixed" quoted-identifiers="mixed" />
|
||||||
|
<secret-storage>master_key</secret-storage>
|
||||||
|
<auth-provider>no-auth</auth-provider>
|
||||||
|
<schema-mapping>
|
||||||
|
<introspection-scope>
|
||||||
|
<node kind="schema" qname="@" />
|
||||||
|
</introspection-scope>
|
||||||
|
</schema-mapping>
|
||||||
|
</data-source>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||||
|
<data-source source="LOCAL" name="articles" uuid="315cb5c9-2b0f-435b-b602-59823b160908">
|
||||||
|
<driver-ref>sqlite.xerial</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/db/articles</jdbc-url>
|
||||||
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
|
</data-source>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
<DataSourcesHistory>
|
||||||
|
<DataSourceFromHistory isRemovedFromProject="true">
|
||||||
|
<data-source source="LOCAL" name="articles" uuid="a0abcd0a-1d6f-40e4-88be-f442bcb431ba">
|
||||||
|
<database-info product="SQLite" version="3.51.1" jdbc-version="4.2" driver-name="SQLite JDBC" driver-version="3.51.1.0" dbms="SQLITE" exact-version="3.51.1" exact-driver-version="3.51">
|
||||||
|
<identifier-quote-string>"</identifier-quote-string>
|
||||||
|
</database-info>
|
||||||
|
<case-sensitivity plain-identifiers="mixed" quoted-identifiers="mixed" />
|
||||||
|
<driver-ref>sqlite.xerial</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/db/articles</jdbc-url>
|
||||||
|
<secret-storage>master_key</secret-storage>
|
||||||
|
<auth-provider>no-auth</auth-provider>
|
||||||
|
<schema-mapping>
|
||||||
|
<introspection-scope>
|
||||||
|
<node kind="schema" qname="@" />
|
||||||
|
</introspection-scope>
|
||||||
|
</schema-mapping>
|
||||||
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
|
</data-source>
|
||||||
|
</DataSourceFromHistory>
|
||||||
|
<DataSourceFromHistory isRemovedFromProject="false">
|
||||||
|
<data-source source="LOCAL" name="articles" uuid="315cb5c9-2b0f-435b-b602-59823b160908">
|
||||||
|
<database-info product="SQLite" version="3.51.1" jdbc-version="4.2" driver-name="SQLite JDBC" driver-version="3.51.1.0" dbms="SQLITE" exact-version="3.51.1" exact-driver-version="3.51">
|
||||||
|
<identifier-quote-string>"</identifier-quote-string>
|
||||||
|
</database-info>
|
||||||
|
<case-sensitivity plain-identifiers="mixed" quoted-identifiers="mixed" />
|
||||||
|
<driver-ref>sqlite.xerial</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/db/articles</jdbc-url>
|
||||||
|
<secret-storage>master_key</secret-storage>
|
||||||
|
<auth-provider>no-auth</auth-provider>
|
||||||
|
<schema-mapping>
|
||||||
|
<introspection-scope>
|
||||||
|
<node kind="schema" qname="@" />
|
||||||
|
</introspection-scope>
|
||||||
|
</schema-mapping>
|
||||||
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
|
</data-source>
|
||||||
|
</DataSourceFromHistory>
|
||||||
|
</DataSourcesHistory>
|
||||||
Generated
+1
@@ -10,6 +10,7 @@
|
|||||||
<option name="highlightLevel" value="WARNING" />
|
<option name="highlightLevel" value="WARNING" />
|
||||||
<option name="transferred" value="true" />
|
<option name="transferred" value="true" />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="PhpProjectSharedConfiguration" php_language_level="8.5" />
|
||||||
<component name="PhpStanOptionsConfiguration">
|
<component name="PhpStanOptionsConfiguration">
|
||||||
<option name="transferred" value="true" />
|
<option name="transferred" value="true" />
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
@@ -1,13 +1,33 @@
|
|||||||
Team Tag-Buchstabe: DI-14-C-Schulte-Eilers-Ortmann
|
# Webprogrammierung: Gruppe DI-14-C
|
||||||
Namen der Studierenden:
|
|
||||||
- Name 1 Caroline Schulte
|
|
||||||
- Name 2 Jacques Eilers
|
|
||||||
- Name 3 Niklas Ortmann
|
|
||||||
Abgabe zu Aufgabenblatt 1
|
|
||||||
|
|
||||||
Hinweise:
|
## Beteiligte
|
||||||
index.html wurde zu einer index.php umbenannt.
|
- Caroline Schulte
|
||||||
webhook.php bitte ignorieren. Diese dient nur als zukünftige Automatisierung des Repo-Pulls (s. https://docs.gitea.com/usage/webhooks)
|
- Jacques Eilers
|
||||||
|
- Niklas Ortmann
|
||||||
|
|
||||||
Beim Verwenden der Navbar-Links bitte Folgendes beachten: nur die Kategorien Physik, Mathe, Informatik führen exemplarisch auf eine Beispiel-Seite.
|
## Login-Informationen für Dummy-User
|
||||||
Die anderen Links sind erstmal Platzhalter, dessen dynamischer Inhalt später mit PHP in die index.php eingefügt wird. Alle anderen Links führen auf eine 404-Seite.
|
`Anmeldename, Passwort, Mailadresse`:
|
||||||
|
- `max.mustermann, test12345, max.mustermann@web.de`
|
||||||
|
|
||||||
|
## Weitere Voraussetzungen zur Nutzung
|
||||||
|
- Per Klick auf das Logo gelangt man auf die Home-Seite.
|
||||||
|
|
||||||
|
## Ausgelassene Teilaufgaben
|
||||||
|
|
||||||
|
## Bekannte Fehler und Mängel
|
||||||
|
- Bitte auf die gesetzten TODO's achten. Wenn Inhalte fehlen, sind sie i.d.R. als TODO kommentiert.
|
||||||
|
- Die Suchseite und Kategorieseite packen momentan alle passenden Beiträge untereinander. Später sollen zunächst 10
|
||||||
|
Ergebnisse auf einer Seite angezeigt werden.
|
||||||
|
- Wenn ein Bild aus einem Beitrag entfernt wird, dann wird noch nicht die Datei im Pfad /uploads gelöscht.
|
||||||
|
- id in showArticle-controller.php und updateArticle-controller.php wird nicht als gültige numerische ID geprüft.
|
||||||
|
- sort in search-results-controller.php wird nicht gegen erlaubte Werte validiert.
|
||||||
|
|
||||||
|
## Besonderheiten des Projektes
|
||||||
|
- Es wurde ein einfacher Beitrags-Editor erstellt. Mit diesem können Beiträge erstellt oder bearbeitet werden.
|
||||||
|
Es handelt es sich um eine einfache Version. Später sollen z.B. Bilder und die Positionierung der Elemente folgen.
|
||||||
|
- Es sind drei Dummy-Beiträge für den Nutzer max.mustermann hinterlegt.
|
||||||
|
- Die Such-Seite umfasst eine Such- und Sortierfunktion. Jedoch fehlt noch eine
|
||||||
|
Filterfunktion (z.B. nur Mathe anzeigen).
|
||||||
|
|
||||||
|
## Sonstiges
|
||||||
|
- Das Datenschema befindet sich unter /planung/Datenschema.pdf
|
||||||
@@ -4,6 +4,14 @@
|
|||||||
-->
|
-->
|
||||||
<main>
|
<main>
|
||||||
|
|
||||||
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "internal_error"): ?>
|
||||||
|
<p class="alert-message is-error">
|
||||||
|
Es ist ein interner Fehler beim Speichern aufgetreten. Bitte versuche es erneut.
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php
|
||||||
|
unset($_SESSION["message"]);
|
||||||
|
?>
|
||||||
<h1>404 - Seite nicht vorhanden</h1>
|
<h1>404 - Seite nicht vorhanden</h1>
|
||||||
<p>
|
<p>
|
||||||
Später im Projekt sollen über index.php?pfad= ... der Inhalt der index.php dynamisch gesetzt werden.
|
Später im Projekt sollen über index.php?pfad= ... der Inhalt der index.php dynamisch gesetzt werden.
|
||||||
|
|||||||
@@ -0,0 +1,271 @@
|
|||||||
|
<?php
|
||||||
|
$user = $user ?? null;
|
||||||
|
if (!isset($_SESSION["user"])) {
|
||||||
|
header("Location: index.php?pfad=login");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!--
|
||||||
|
Seite: Beitrag erstellen
|
||||||
|
Inhalt: Formular für die Erstellung eines neuen Beitrags
|
||||||
|
-->
|
||||||
|
<form method="post" action="php/controller/createArticle-controller.php" id="editor-form" class="article-editor-scope.editor-container article-editor-scope editor-container">
|
||||||
|
|
||||||
|
<main class="editor-main">
|
||||||
|
<?php include_once "includes/alertMessages.php"?>
|
||||||
|
|
||||||
|
<input type="text" id="title" name="title"
|
||||||
|
value="<?php echo htmlspecialchars($_SESSION['old_title'] ?? ''); unset($_SESSION['old_title']); ?>"
|
||||||
|
placeholder="Titel hier eingeben" required>
|
||||||
|
|
||||||
|
<!-- Hier werden die dynamischen divs via JavaScript eingefügt -->
|
||||||
|
<div id="block-container"></div>
|
||||||
|
|
||||||
|
<!-- Plus-Button und das Pop-up-Menü -->
|
||||||
|
<div id="add-block-control" class="article-editor-scope add-block-control">
|
||||||
|
<button type="button" id="plus-button" class="article-editor-scope plus-button">+</button>
|
||||||
|
<div id="block-popup" class="article-editor-scope block-popup hidden">
|
||||||
|
<button type="button" data-type="text">Textblock</button>
|
||||||
|
<button type="button" data-type="image">Bild einfügen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Unsichtbares Textfeld, das die JSON-Daten hält und an den Controller postet -->
|
||||||
|
<textarea id="content" name="content" style="display:none;"><?php
|
||||||
|
if (isset($_SESSION['old_content']) && !empty($_SESSION['old_content'])){
|
||||||
|
echo htmlspecialchars($_SESSION['old_content']);
|
||||||
|
unset($_SESSION['old_content']);
|
||||||
|
} else {
|
||||||
|
echo '[]'; // Standardmäßig ein leeres JSON-Array
|
||||||
|
}
|
||||||
|
?></textarea>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Seitenleiste -->
|
||||||
|
<aside class="editor-sidebar">
|
||||||
|
|
||||||
|
<div class="sidebar-block">
|
||||||
|
<button type="submit" id="editor-button" class="button">Veröffentlichen</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-block">
|
||||||
|
<label for="category">Kategorie <span class="required">*</span></label>
|
||||||
|
<select id="category" name="category" required>
|
||||||
|
<option disabled <?php
|
||||||
|
if ((!isset($_SESSION['old_category']) || empty($_SESSION['old_category'])) && (!isset($category) || empty($category))) {
|
||||||
|
echo 'selected';
|
||||||
|
}
|
||||||
|
?>>Kategorie wählen...</option>
|
||||||
|
|
||||||
|
<optgroup label="Sprachen">
|
||||||
|
<option value="deutsch" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'deutsch') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'deutsch') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Deutsch</option>
|
||||||
|
<option value="englisch" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'englisch') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'englisch') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Englisch</option>
|
||||||
|
<option value="franzoesisch" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'franzoesisch') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'franzoesisch') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Französisch</option>
|
||||||
|
<option value="latein" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'latein') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'latein') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Latein</option>
|
||||||
|
<option value="literatur" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'literatur') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'literatur') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Literatur</option>
|
||||||
|
</optgroup>
|
||||||
|
|
||||||
|
<optgroup label="MINT">
|
||||||
|
<option value="mathe" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'mathe') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'mathe') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Mathematik</option>
|
||||||
|
<option value="biologie" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'biologie') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'biologie') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Biologie</option>
|
||||||
|
<option value="chemie" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'chemie') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'chemie') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Chemie</option>
|
||||||
|
<option value="physik" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'physik') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'physik') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Physik</option>
|
||||||
|
<option value="informatik" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'informatik') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'informatik') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Informatik</option>
|
||||||
|
<option value="astronomie" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'astronomie') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'astronomie') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Astronomie</option>
|
||||||
|
</optgroup>
|
||||||
|
|
||||||
|
<optgroup label="Gesellschaft & Werte">
|
||||||
|
<option value="geschichte" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'geschichte') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'geschichte') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Geschichte</option>
|
||||||
|
<option value="erdkunde" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'erdkunde') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'erdkunde') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Erdkunde</option>
|
||||||
|
<option value="sozialkunde" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'sozialkunde') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'sozialkunde') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Sozialkunde</option>
|
||||||
|
<option value="wirtschaft" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'wirtschaft') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'wirtschaft') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Wirtschaftskunde</option>
|
||||||
|
<option value="religion" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'religion') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'religion') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Religion</option>
|
||||||
|
<option value="ethik" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'ethik') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'ethik') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Ethikunterricht</option>
|
||||||
|
<option value="philosophie" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'philosophie') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'philosophie') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Philosophie</option>
|
||||||
|
<option value="psychologie" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'psychologie') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'psychologie') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Psychologie</option>
|
||||||
|
<option value="kunst" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'kunst') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'kunst') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Kunst</option>
|
||||||
|
<option value="musik" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'musik') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'musik') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Musik</option>
|
||||||
|
<option value="theater" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'theater') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'theater') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Theater</option>
|
||||||
|
</optgroup>
|
||||||
|
|
||||||
|
<optgroup label="Technik & Praxis">
|
||||||
|
<option value="technik" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'technik') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'technik') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Technik</option>
|
||||||
|
<option value="werken" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'werken') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'werken') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Werken</option>
|
||||||
|
<option value="hauswirtschaft" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'hauswirtschaft') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'hauswirtschaft') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Hauswirtschaft</option>
|
||||||
|
<option value="sport" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'sport') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'sport') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Sport</option>
|
||||||
|
</optgroup>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
if (isset($_SESSION['old_category'])) {
|
||||||
|
unset($_SESSION['old_category']);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-block">
|
||||||
|
<label for="tags">Schlagwörter</label>
|
||||||
|
<input type="text" id="tags" name="tags"
|
||||||
|
value="<?php echo htmlspecialchars($_SESSION['old_tags'] ?? ''); unset($_SESSION['old_tags']); ?>"
|
||||||
|
placeholder="z.B. Technik, IT (mit Komma trennen)">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
</form>
|
||||||
+33
-21
@@ -1,8 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
include_once 'php/controller/home-controller.php';
|
||||||
|
?>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Seite: Home
|
Seite: Home
|
||||||
Inhalt: Beinhaltet den Inhalt der Startseite
|
Inhalt: Beinhaltet den Inhalt der Startseite
|
||||||
-->
|
-->
|
||||||
<main>
|
<main>
|
||||||
|
<?php include_once "includes/alertMessages.php"?>
|
||||||
|
|
||||||
<h1>Home</h1>
|
<h1>Home</h1>
|
||||||
<p>
|
<p>
|
||||||
@@ -14,27 +19,34 @@
|
|||||||
sea takimata sanctus est Lorem ipsum dolor sit amet.
|
sea takimata sanctus est Lorem ipsum dolor sit amet.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- Flexbox -->
|
<!-- Flexbox für aktive Kategorien -->
|
||||||
<div class="flexbox">
|
<div class="flexbox">
|
||||||
<div class="container">
|
<?php
|
||||||
<a href="index.php?pfad=informatik" class="category-link">Informatik</a>
|
if (!empty($categoriesWithArticles) && is_array($categoriesWithArticles)):
|
||||||
<div class="article-link">
|
foreach ($categoriesWithArticles as $category):
|
||||||
<a href="datenschutzVSdatensicherheit.php">Datenschutz vs. Datensicherheit</a>
|
?>
|
||||||
</div>
|
<div class="container">
|
||||||
</div>
|
<a href="index.php?pfad=showCategory&category=<?php echo htmlspecialchars($category['slug']); ?>" class="category-link">
|
||||||
|
<?php echo htmlspecialchars($category['name']); ?>
|
||||||
|
</a>
|
||||||
|
|
||||||
<div class="container">
|
<!-- die 5 neuesten Beiträge der Kategorie -->
|
||||||
<a href="index.php?pfad=mathe" class="category-link">Mathe</a>
|
<div class="article-links-wrapper">
|
||||||
<div class="article-link">
|
<?php foreach ($category['articles'] as $article): ?>
|
||||||
<a href="pythagoras.php">Satz des Pythagoras</a>
|
<div class="article-link">
|
||||||
</div>
|
<a href="index.php?pfad=showArticle&id=<?php echo htmlspecialchars($article->getId()); ?>">
|
||||||
</div>
|
<?php echo htmlspecialchars($article->getTitle()); ?>
|
||||||
|
</a>
|
||||||
<div class="container">
|
</div>
|
||||||
<a href="index.php?pfad=physik" class="category-link">Physik</a>
|
<?php endforeach; ?>
|
||||||
<div class="article-link">
|
</div>
|
||||||
<a href="tunneleffekt.php">Der Tunneleffekt</a>
|
</div>
|
||||||
</div>
|
<?php
|
||||||
</div>
|
endforeach;
|
||||||
|
else:
|
||||||
|
?>
|
||||||
|
<p>Aktuell sind keine Beiträge in den Kategorien vorhanden.</p>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
<!--
|
|
||||||
Seite: Informatik
|
|
||||||
Inhalt: Eine Übersicht über alle Beiträge zum Fach
|
|
||||||
-->
|
|
||||||
<main>
|
|
||||||
|
|
||||||
<h1>Informatik</h1>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et
|
|
||||||
dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
|
|
||||||
clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet,
|
|
||||||
consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
|
|
||||||
sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no
|
|
||||||
sea takimata sanctus est Lorem ipsum dolor sit amet.
|
|
||||||
</p>
|
|
||||||
<h2>Artikel:</h2>
|
|
||||||
<div>
|
|
||||||
<a href="datenschutzVSdatensicherheit.php">Datenschutz vs. Datensicherheit</a>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
+34
-13
@@ -1,32 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
$error = $error ?? null;
|
||||||
|
?>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Form: Login-Bereich
|
Form: Login-Bereich
|
||||||
Funktion: Benutzerauthentifizierung und Zugang zum eigenen Profil, Erstellen von Beiträgen, etc.
|
Funktion: Benutzerauthentifizierung und Zugang zum eigenen Profil, Erstellen von Beiträgen, etc.
|
||||||
-->
|
-->
|
||||||
<main class="login-page">
|
<main class="login-page">
|
||||||
<div class="login-container">
|
<div class="login-container">
|
||||||
|
|
||||||
<h1>Bitte anmelden</h1>
|
<h1>Bitte anmelden</h1>
|
||||||
|
|
||||||
<form>
|
<?php if ($error): ?>
|
||||||
<label id ="inputEmail" class="screenreader-only">E-Mail Adresse / </label>
|
<p style="color:red;">
|
||||||
|
<?php echo htmlspecialchars($error); ?>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<form method="post" action="index.php?pfad=login">
|
||||||
|
|
||||||
<p class="input-label">Benutzername/E-Mail-Adresse:</p>
|
<p class="input-label">Benutzername/E-Mail-Adresse:</p>
|
||||||
<input type="email" name="email" class="login-input" placeholder="E-Mail-Adresse" required autofocus>
|
<input type="email"
|
||||||
|
name="email"
|
||||||
|
class="login-input"
|
||||||
|
placeholder="E-Mail-Adresse"
|
||||||
|
required
|
||||||
|
autofocus>
|
||||||
|
|
||||||
<p class="input-label">Passwort:</p>
|
<p class="input-label">Passwort:</p>
|
||||||
<input type="password" name="password" class="login-input" placeholder="Passwort" required>
|
<input type="password"
|
||||||
|
name="password"
|
||||||
|
class="login-input"
|
||||||
|
placeholder="Passwort"
|
||||||
|
required>
|
||||||
|
|
||||||
<div class="checkbox-wrapper">
|
<button type="submit"
|
||||||
<label>
|
value="anmelden"
|
||||||
<input type="checkbox" name="remember-me" value="1"> angemeldet bleiben
|
name="loginSubmit"
|
||||||
</label>
|
class="button">
|
||||||
</div>
|
anmelden
|
||||||
|
</button>
|
||||||
<button type="submit" value="anmelden" name="loginSubmit" class="login-button">anmelden</button>
|
|
||||||
|
|
||||||
<div class="register-link">
|
<div class="register-link">
|
||||||
<a href="register.php">Noch keinen Account? Jetzt hier registrieren!</a>
|
<a href="index.php?pfad=register">
|
||||||
|
Noch keinen Account? Jetzt hier registrieren!
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
<!--
|
|
||||||
Seite: Mathe
|
|
||||||
Inhalt: Eine Übersicht über alle Beiträge zum Fach
|
|
||||||
-->
|
|
||||||
<main>
|
|
||||||
|
|
||||||
<h1>Mathe</h1>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et
|
|
||||||
dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
|
|
||||||
clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet,
|
|
||||||
consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
|
|
||||||
sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no
|
|
||||||
sea takimata sanctus est Lorem ipsum dolor sit amet.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>Artikel:</h2>
|
|
||||||
<div>
|
|
||||||
<a href="pythagoras.php">Satz des Pythagoras</a>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
@@ -10,10 +10,10 @@
|
|||||||
<meta name="description" content="EduForge">
|
<meta name="description" content="EduForge">
|
||||||
<meta name="author" content="Niklas Ortmann">
|
<meta name="author" content="Niklas Ortmann">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="icon" type="image/x-icon" href="images/logos/logo_icon.ico">
|
<link rel="icon" type="image/x-icon" href="../images/logos/logo_icon.ico">
|
||||||
<link rel="stylesheet" href="css/main.css">
|
<link rel="stylesheet" href="../css/main.css">
|
||||||
<link rel="stylesheet" href="css/navbar.css">
|
<link rel="stylesheet" href="../css/navbar.css">
|
||||||
<link rel="stylesheet" href="css/footer.css">
|
<link rel="stylesheet" href="../css/footer.css">
|
||||||
<title>EduForge</title>
|
<title>EduForge</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<!--
|
|
||||||
Seite: Physik
|
|
||||||
Inhalt: Eine Übersicht über alle Beiträge zum Fach
|
|
||||||
-->
|
|
||||||
<main>
|
|
||||||
|
|
||||||
<h1>Physik</h1>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et
|
|
||||||
dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
|
|
||||||
clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet,
|
|
||||||
consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
|
|
||||||
sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no
|
|
||||||
sea takimata sanctus est Lorem ipsum dolor sit amet.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>Artikel:</h2>
|
|
||||||
<div>
|
|
||||||
<a href="tunneleffekt.php">Der Tunneleffekt</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</main>
|
|
||||||
+215
-29
@@ -1,46 +1,232 @@
|
|||||||
<!--
|
<?php
|
||||||
Content: Profil
|
include_once 'php/controller/profile-controller.php';
|
||||||
Inhalt: Das eigene Profil, wenn man angemeldet ist. Dort hat man die Möglichkeit seine Angaben zu ändern.
|
|
||||||
-->
|
$user = $user ?? null;
|
||||||
|
$isEditMode = (isset($_GET["edit"]) && $_GET["edit"] === "1") || !empty($error);
|
||||||
|
?>
|
||||||
|
|
||||||
<main class="form-page">
|
<main class="form-page">
|
||||||
|
<div class="flexbox">
|
||||||
|
<div class="container">
|
||||||
|
<?php include_once "includes/alertMessages.php" ?>
|
||||||
|
|
||||||
<div class="form-container">
|
<?php if (!empty($error)): ?>
|
||||||
<form>
|
<p class="alert-message is-error">
|
||||||
|
<?php echo htmlspecialchars($error); ?>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<label class="input-label">Vorname</label>
|
<form method="post" action="index.php?pfad=profile">
|
||||||
|
|
||||||
<input type="text" name="Vorname" class="login-input" required
|
<label class="input-label">Vorname</label>
|
||||||
placeholder="Vorname"
|
<input type="text"
|
||||||
value="<?php //DB-Daten ?>">
|
name="vorname"
|
||||||
|
class="login-input"
|
||||||
|
value="<?php echo htmlspecialchars($_POST["vorname"] ?? $user["vorname"] ?? ""); ?>"
|
||||||
|
<?php echo $isEditMode ? "" : "readonly"; ?>
|
||||||
|
required>
|
||||||
|
|
||||||
<label class="input-label">Nachname</label>
|
<label class="input-label">Nachname</label>
|
||||||
<input type="text" name="Nachname" class="login-input" required
|
<input type="text"
|
||||||
placeholder="Nachname"
|
name="nachname"
|
||||||
value="<?php //DB-Daten ?>">
|
class="login-input"
|
||||||
|
value="<?php echo htmlspecialchars($_POST["nachname"] ?? $user["nachname"] ?? ""); ?>"
|
||||||
|
<?php echo $isEditMode ? "" : "readonly"; ?>
|
||||||
|
required>
|
||||||
|
|
||||||
<label class="input-label">Email-Adresse</label>
|
<label class="input-label">Email-Adresse</label>
|
||||||
<input type="email" name="Email" class="login-input" required
|
<input type="email"
|
||||||
placeholder="mustermann@web.de"
|
name="email"
|
||||||
value="<?php //DB-Daten ?>">
|
class="login-input"
|
||||||
|
value="<?php echo htmlspecialchars($_POST["email"] ?? $user["email"] ?? ""); ?>"
|
||||||
|
<?php echo $isEditMode ? "" : "readonly"; ?>
|
||||||
|
required>
|
||||||
|
|
||||||
<label class="input-label">Passwort</label>
|
<label class="input-label">
|
||||||
<input type="password" name="Passwort" class="login-input"
|
<?php echo $isEditMode ? "Neues Passwort" : "Passwort"; ?>
|
||||||
required placeholder="Passwort">
|
</label>
|
||||||
|
|
||||||
|
<?php if ($isEditMode): ?>
|
||||||
|
<input type="password"
|
||||||
|
name="password"
|
||||||
|
class="login-input"
|
||||||
|
placeholder="Leer lassen, wenn es gleich bleiben soll">
|
||||||
|
<?php else: ?>
|
||||||
|
<input type="password"
|
||||||
|
name="password"
|
||||||
|
class="login-input"
|
||||||
|
value="********"
|
||||||
|
readonly>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
<?php if ($isEditMode): ?>
|
||||||
|
<button type="submit"
|
||||||
|
name="saveProfile"
|
||||||
|
class="button">
|
||||||
|
Speichern
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<a href="index.php?pfad=profile" class="button">
|
||||||
|
Abbrechen
|
||||||
|
</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<a href="index.php?pfad=profile&edit=1" class="button">
|
||||||
|
Bearbeiten
|
||||||
|
</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<button type="submit" class="login-button">
|
<form action="php/controller/deleteAccount-controller.php" method="POST">
|
||||||
Speichern
|
<button type="submit"
|
||||||
</button>
|
class="button"
|
||||||
|
onclick="return confirm('Möchtest du deinen Account wirklich unwiderruflich löschen?');">
|
||||||
|
Account löschen
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
<br><br>
|
</div>
|
||||||
|
|
||||||
<button type="button" class="login-button">
|
<div class="container">
|
||||||
Account löschen
|
|
||||||
</button>
|
|
||||||
|
|
||||||
</form>
|
<h2 class="section-title">Meine Beiträge</h2>
|
||||||
|
|
||||||
|
<div class="articles-list">
|
||||||
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "internal_error"): ?>
|
||||||
|
|
||||||
|
<p class="alert-message is-error">
|
||||||
|
Es ist ein interner Fehler aufgetreten. Bitte versuche es erneut.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<?php elseif (isset($userArticles) && count($userArticles) > 0): ?>
|
||||||
|
|
||||||
|
<?php foreach ($userArticles as $userArticle): ?>
|
||||||
|
<div class="article-item">
|
||||||
|
<div class="article-meta">
|
||||||
|
<span class="article-date">
|
||||||
|
<?php echo htmlspecialchars($userArticle->getCreationDate()); ?>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="article-category">
|
||||||
|
<?php echo htmlspecialchars($userArticle->getCategory()); ?>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="article-title">
|
||||||
|
<a href="index.php?pfad=showArticle&id=<?php echo $userArticle->getID(); ?>">
|
||||||
|
<?php echo htmlspecialchars($userArticle->getTitle()); ?>
|
||||||
|
</a>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$tags = $userArticle->getTags();
|
||||||
|
if (isset($tags) && !empty($tags)):
|
||||||
|
?>
|
||||||
|
<div class="article-view-bottom-section">
|
||||||
|
<div class="article-view-tags-label">Tags:</div>
|
||||||
|
|
||||||
|
<div class="article-view-tags-list">
|
||||||
|
<?php
|
||||||
|
$tagArray = is_array($tags) ? $tags : explode(',', $tags);
|
||||||
|
|
||||||
|
foreach ($tagArray as $tag):
|
||||||
|
$trimmedTag = trim($tag);
|
||||||
|
|
||||||
|
if (!empty($trimmedTag)):
|
||||||
|
?>
|
||||||
|
<span class="article-view-tag-item">
|
||||||
|
<?php echo htmlspecialchars($trimmedTag); ?>
|
||||||
|
</span>
|
||||||
|
<?php
|
||||||
|
endif;
|
||||||
|
endforeach;
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<a href="index.php?pfad=updateArticle&id=<?php echo $userArticle->getID(); ?>"
|
||||||
|
class="edit-link-button">
|
||||||
|
Bearbeiten
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<form action="php/controller/deleteArticle-controller.php" method="POST">
|
||||||
|
<input type="hidden"
|
||||||
|
name="id"
|
||||||
|
value="<?php echo htmlspecialchars($userArticle->getID()); ?>">
|
||||||
|
|
||||||
|
<button type="submit"
|
||||||
|
class="button"
|
||||||
|
onclick="return confirm('Möchtest du diesen Artikel wirklich löschen?');">
|
||||||
|
Löschen
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<p>Du hast noch keine Beiträge erstellt.</p>
|
||||||
|
|
||||||
|
<button type="button"
|
||||||
|
class="button"
|
||||||
|
onclick="window.location.href='index.php?pfad=createArticle';">
|
||||||
|
Beitrag erstellen!
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="comments-section">
|
||||||
|
|
||||||
|
<h2 class="section-title">Meine Kommentare</h2>
|
||||||
|
|
||||||
|
<!-- Liste aller Kommentare des Nutzers -->
|
||||||
|
<div class="comments-list">
|
||||||
|
|
||||||
|
<?php if (isset($userComments) && count($userComments) > 0): ?>
|
||||||
|
|
||||||
|
<?php foreach ($userComments as $comment): ?>
|
||||||
|
<div class="article-item">
|
||||||
|
|
||||||
|
<!-- Erstellungsdatum des Kommentars -->
|
||||||
|
<div class="article-meta">
|
||||||
|
<span class="article-date">
|
||||||
|
<?php echo htmlspecialchars($comment->getCreated()); ?>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Inhalt des Kommentars -->
|
||||||
|
<p>
|
||||||
|
<?php echo nl2br(htmlspecialchars($comment->getContent())); ?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Link zum Beitrag, unter dem der Kommentar geschrieben wurde -->
|
||||||
|
<a href="index.php?pfad=showArticle&id=<?php echo htmlspecialchars($comment->getArticleId()); ?>"
|
||||||
|
class="edit-link-button">
|
||||||
|
Zum Beitrag
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<p>Du hast noch keine Kommentare geschrieben.</p>
|
||||||
|
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php unset($_SESSION["message"]); ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
+42
-11
@@ -1,3 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
$error = $error ?? null;
|
||||||
|
?>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Form: Registrierung
|
Form: Registrierung
|
||||||
Funktion: Erstellung neuer Benutzerkonten
|
Funktion: Erstellung neuer Benutzerkonten
|
||||||
@@ -7,26 +11,53 @@
|
|||||||
|
|
||||||
<h1>Jetzt Registrieren!</h1>
|
<h1>Jetzt Registrieren!</h1>
|
||||||
|
|
||||||
<form>
|
<?php if (!empty($error)): ?>
|
||||||
|
<p class="alert-message is-error" style="color:red;">
|
||||||
|
<?php echo htmlspecialchars($error); ?>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<form method="post" action="index.php?pfad=register">
|
||||||
|
|
||||||
<p class="input-label">Email:</p>
|
<p class="input-label">Email:</p>
|
||||||
<input type="email" name="email" class="login-input" placeholder="mustermann@web.de" required>
|
<input type="email"
|
||||||
|
name="email"
|
||||||
|
class="login-input"
|
||||||
|
placeholder="mustermann@web.de"
|
||||||
|
value="<?php echo htmlspecialchars($_POST["email"] ?? ""); ?>"
|
||||||
|
required>
|
||||||
|
|
||||||
<p class="input-label">Vorname:</p>
|
<p class="input-label">Vorname:</p>
|
||||||
<input type="text" name="vorname" class="login-input" placeholder="Max" required>
|
<input type="text"
|
||||||
|
name="vorname"
|
||||||
|
class="login-input"
|
||||||
|
placeholder="Max"
|
||||||
|
value="<?php echo htmlspecialchars($_POST["vorname"] ?? ""); ?>"
|
||||||
|
required>
|
||||||
|
|
||||||
<p class="input-label">Nachname:</p>
|
<p class="input-label">Nachname:</p>
|
||||||
<input type="text" name="nachname" class="login-input" placeholder="Mustermann" required>
|
<input type="text"
|
||||||
|
name="nachname"
|
||||||
|
class="login-input"
|
||||||
|
placeholder="Mustermann"
|
||||||
|
value="<?php echo htmlspecialchars($_POST["nachname"] ?? ""); ?>"
|
||||||
|
required>
|
||||||
|
|
||||||
<p class="input-label">Passwort:</p>
|
<p class="input-label">Passwort:</p>
|
||||||
<input type="password" name="password" class="login-input" placeholder="Passwort" required>
|
<input type="password"
|
||||||
|
name="password"
|
||||||
|
class="login-input"
|
||||||
|
placeholder="Passwort"
|
||||||
|
required>
|
||||||
|
|
||||||
<div class="checkbox-wrapper">
|
<button type="submit"
|
||||||
<label>
|
value="register"
|
||||||
<input type="checkbox" value="remember-me"> angemeldet bleiben
|
name="registerSubmit"
|
||||||
</label>
|
class="button">
|
||||||
</div>
|
kostenlos registrieren
|
||||||
|
</button>
|
||||||
|
|
||||||
<button type="submit" value="anmelden" name="loginSubmit" class="login-button">kostenlos registrieren</button>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
<?php
|
||||||
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
$all_results = $_SESSION["search_results"] ?? [];
|
||||||
|
$query = $_SESSION["search_query"] ?? "";
|
||||||
|
$totalResultsCount = count($all_results);
|
||||||
|
|
||||||
|
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 10;
|
||||||
|
if (!in_array($limit, [10, 20, 50, 100])) {
|
||||||
|
$limit = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gesamtseitenzahl
|
||||||
|
$totalPages = max(1, ceil($totalResultsCount / $limit));
|
||||||
|
|
||||||
|
// Aktuelle Seite auslesen und validieren
|
||||||
|
$currentPage = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||||
|
if ($currentPage < 1) {
|
||||||
|
$currentPage = 1;
|
||||||
|
} elseif ($currentPage > $totalPages) {
|
||||||
|
$currentPage = $totalPages;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Startpunkt im Array berechnen (Offset)
|
||||||
|
$offset = ($currentPage - 1) * $limit;
|
||||||
|
|
||||||
|
// Nur die Ergebnisse für die aktuelle Seite ausschneiden
|
||||||
|
//$results = array_slice($all_results, $offset, $limit);
|
||||||
|
$results = $all_results;
|
||||||
|
$resultCount = count($results);
|
||||||
|
|
||||||
|
?>
|
||||||
|
<noscript>
|
||||||
|
Bitte JavaScript aktivieren!
|
||||||
|
</noscript>
|
||||||
|
<!--
|
||||||
|
Seite: Suchergebnisse
|
||||||
|
Inhalt: Zeigt die Ergebnisse einer Suche an
|
||||||
|
-->
|
||||||
|
<div class="s-res-layout-grid">
|
||||||
|
<?php include_once "includes/alertMessages.php"?>
|
||||||
|
|
||||||
|
<!-- Links: Seitenleiste für Filter und Suche -->
|
||||||
|
<aside class="s-res-sidebar">
|
||||||
|
|
||||||
|
<form action="php/controller/search-results-controller.php" method="GET" id="search-form-id" class="s-res-sidebar-form">
|
||||||
|
|
||||||
|
<input type="hidden" id="s-res-page-input" name="page" value="<?php echo $_GET['page'] ?? 1; ?>">
|
||||||
|
|
||||||
|
<div class="s-res-sidebar-box">
|
||||||
|
<h3 class="s-res-sidebar-title">Suche anpassen</h3>
|
||||||
|
<input type="search" id="site-search" name="q" placeholder="Suchen..." class="nav__search" value="<?php echo htmlspecialchars($query); ?>" maxlength="50" required>
|
||||||
|
<button type="submit" class="nav__search-button">Suchen</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="s-res-sidebar-box">
|
||||||
|
<h3 class="s-res-sidebar-title">Sortierung</h3>
|
||||||
|
<?php $currentSort = $_SESSION['search_sort'] ?? 'alphabet'; ?>
|
||||||
|
<div class="s-res-filter-group">
|
||||||
|
<label class="s-res-filter-option">
|
||||||
|
<input type="radio" name="sort" value="alphabet" class="sort-radio" <?php echo $currentSort === 'alphabet' ? 'checked' : ''; ?>>
|
||||||
|
<span>Alphabetisch</span>
|
||||||
|
</label>
|
||||||
|
<label class="s-res-filter-option">
|
||||||
|
<input type="radio" name="sort" value="likes" class="sort-radio" <?php echo $currentSort === 'likes' ? 'checked' : ''; ?>>
|
||||||
|
<span>Beliebtheit (Likes)</span>
|
||||||
|
</label>
|
||||||
|
<label class="s-res-filter-option">
|
||||||
|
<input type="radio" name="sort" value="newest" class="sort-radio" <?php echo $currentSort === 'newest' ? 'checked' : ''; ?>>
|
||||||
|
<span>Neueste Beiträge</span>
|
||||||
|
</label>
|
||||||
|
<label class="s-res-filter-option">
|
||||||
|
<input type="radio" name="sort" value="oldest" class="sort-radio" <?php echo $currentSort === 'oldest' ? 'checked' : ''; ?>>
|
||||||
|
<span>Älteste Beiträge</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="s-res-sidebar-box">
|
||||||
|
<h3 class="s-res-sidebar-title">Kategorie filtern</h3>
|
||||||
|
<select id="category-filter" class="s-res-limit-select" style="width: 100%; padding: 8px; border-radius: 6px; border: 1px solid #cbd5e1;">
|
||||||
|
<option value="all">Alle Kategorien</option>
|
||||||
|
<option value="Deutsch">Deutsch</option>
|
||||||
|
<option value="Englisch">Englisch</option>
|
||||||
|
<option value="Franzoesisch">Französisch</option>
|
||||||
|
<option value="Latein">Latein</option>
|
||||||
|
<option value="Literatur">Literatur</option>
|
||||||
|
<option value="Mathematik">Mathematik</option>
|
||||||
|
<option value="Biologie">Biologie</option>
|
||||||
|
<option value="Informatik">Informatik</option>
|
||||||
|
<option value="Chemie">Chemie</option>
|
||||||
|
<option value="Physik">Physik</option>
|
||||||
|
<option value="Astronomie">Astronomie</option>
|
||||||
|
<option value="Geschichte">Geschichte</option>
|
||||||
|
<option value="Erdkunde">Erdkunde</option>
|
||||||
|
<option value="Sozialkunde">Sozialkunde</option>
|
||||||
|
<option value="Wirtschaftskunde">Wirtschaftskunde</option>
|
||||||
|
<option value="Religion">Religion</option>
|
||||||
|
<option value="Ethikunterricht">Ethikunterricht</option>
|
||||||
|
<option value="Philosophie">Philosophie</option>
|
||||||
|
<option value="Psychologie">Psychologie</option>
|
||||||
|
<option value="Kunst">Kunst</option>
|
||||||
|
<option value="Musik">Musik</option>
|
||||||
|
<option value="Theater">Theater</option>
|
||||||
|
<option value="Technik">Technik</option>
|
||||||
|
<option value="Werken">Werken</option>
|
||||||
|
<option value="Hauswirtschaft">Hauswirtschaft</option>
|
||||||
|
<option value="Sport">Sport</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<main class="s-res-main-content">
|
||||||
|
|
||||||
|
<div class="s-res-header">
|
||||||
|
<h1 class="s-res-main-title">Suchergebnisse</h1>
|
||||||
|
<p class="s-res-meta"><?php echo $totalResultsCount; ?> Treffer für Ihre Suchanfrage "<?php echo htmlspecialchars($query); ?>"</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Ergebnisliste -->
|
||||||
|
<div class="s-res-list">
|
||||||
|
<?php
|
||||||
|
if (!empty($results)): ?>
|
||||||
|
|
||||||
|
<?php foreach ($results as $item):
|
||||||
|
// Anzahl der Likes ermitteln (falls es ein Array ist, zählen; falls Zahl, direkt nutzen)
|
||||||
|
$likesCount = isset($item['likes']) && is_array($item['likes']) ? count($item['likes']) : ($item['likes'] ?? 0);
|
||||||
|
?>
|
||||||
|
<div class="s-res-item" data-likes="<?php echo $likesCount; ?>" data-category="<?php echo strtolower($item['category'] ?? ''); ?>">
|
||||||
|
<div class="s-res-content">
|
||||||
|
<h2 class="s-res-item-title">
|
||||||
|
<a href="index.php?pfad=showArticle&id=<?php echo $item['id']; ?>" class="s-res-link">
|
||||||
|
<?php echo htmlspecialchars($item['title']); ?>
|
||||||
|
</a>
|
||||||
|
</h2>
|
||||||
|
<div class="s-res-meta-row">
|
||||||
|
<p class="s-res-author">Von: <span class="s-res-author-name"><?php echo htmlspecialchars($item['author']); ?></span></p>
|
||||||
|
|
||||||
|
<span class="s-res-likes">
|
||||||
|
❤️ <?php echo $likesCount; ?>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="s-res-arrow">→</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
elseif (isset($_SESSION["search_query"]) && $_SESSION["search_query"] !== "" && $resultCount === 0): ?>
|
||||||
|
<p>Keine Beiträge zu diesem Suchbegriff gefunden.</p>
|
||||||
|
<?php
|
||||||
|
elseif (isset($_SESSION["message"]) && $_SESSION["message"] == "invalid_search_query"): ?>
|
||||||
|
<p>Unzulässige Suchanfrage</p>
|
||||||
|
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php
|
||||||
|
unset($_SESSION["message"]);
|
||||||
|
?>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="s-res-pagination-footer">
|
||||||
|
|
||||||
|
<!-- Auswahl der Ergebnisse pro Seite -->
|
||||||
|
<div class="s-res-limit-selector">
|
||||||
|
<label for="s-res-per-page" class="s-res-limit-label">Ergebnisse pro Seite:</label>
|
||||||
|
<select id="s-res-per-page" name="limit" class="s-res-limit-select" form="search-form-id" onchange="this.form.submit()">
|
||||||
|
<option value="10" <?php echo $limit === 10 ? 'selected' : ''; ?>>10</option>
|
||||||
|
<option value="20" <?php echo $limit === 20 ? 'selected' : ''; ?>>20</option>
|
||||||
|
<option value="50" <?php echo $limit === 50 ? 'selected' : ''; ?>>50</option>
|
||||||
|
<option value="100" <?php echo $limit === 100 ? 'selected' : ''; ?>>100</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="s-res-page-navigation">
|
||||||
|
<button type="button" class="s-res-page-btn" id="prev-page-btn" data-page="0">«</button>
|
||||||
|
|
||||||
|
<span id="dynamic-page-numbers"></span>
|
||||||
|
|
||||||
|
<button type="button" class="s-res-page-btn" id="next-page-btn" data-page="2">»</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
<?php
|
||||||
|
$comments = [];
|
||||||
|
$mainComments = [];
|
||||||
|
$repliesByParent = [];
|
||||||
|
$articleObj = null;
|
||||||
|
|
||||||
|
include_once 'php/controller/showArticle-controller.php';
|
||||||
|
?>
|
||||||
|
<!--
|
||||||
|
Seite: Anzeige für Beiträge
|
||||||
|
Funktion: Stellt einen übergebenen Beitrag dar.
|
||||||
|
-->
|
||||||
|
<!-- Hauptcontainer für die Beitragsansicht (Ausschließlich der Content-Bereich) -->
|
||||||
|
<main class="article-view-container">
|
||||||
|
<?php include_once "includes/alertMessages.php"?>
|
||||||
|
|
||||||
|
<!-- Metadaten & Titel -->
|
||||||
|
<div class="article-view-top-section">
|
||||||
|
|
||||||
|
<div class="article-view-top-section">
|
||||||
|
|
||||||
|
<div class="category-and-likes-row">
|
||||||
|
<?php if (isset($category) && !empty($category)): ?>
|
||||||
|
<span class="article-view-category"><?php echo htmlspecialchars($category); ?></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<!-- Like-Anzeige und dynamischer Like-Button -->
|
||||||
|
<?php if (isset($articleObj) && $articleObj !== null): ?>
|
||||||
|
<div class="article-view-likes">
|
||||||
|
<span>❤️ <span class="like-count"><?php echo $articleObj->getLikeCount(); ?></span></span>
|
||||||
|
|
||||||
|
<?php if (isset($_SESSION["user_email"])): ?>
|
||||||
|
<a href="php/controller/like-controller.php?id=<?php echo $articleObj->getId(); ?>" class="like-toggle-btn">
|
||||||
|
<?php echo $articleObj->hasLiked($_SESSION["user_email"]) ? '👎 Gefällt mir nicht mehr' : '👍 Gefällt mir'; ?>
|
||||||
|
</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<span class="login-hint">(Anmelden zum Liken)</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 class="article-view-title">
|
||||||
|
<?php if (isset($title)) { echo htmlspecialchars($title); } ?>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div class="article-view-meta">
|
||||||
|
<?php if (isset($author) && !empty($author)): ?>
|
||||||
|
<span class="article-view-author">Von: <strong><?php echo htmlspecialchars($author); ?></strong></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Beitrags-Inhalt -->
|
||||||
|
<div class="article-view-content">
|
||||||
|
<?php if (isset($content)): ?>
|
||||||
|
<?php
|
||||||
|
// Versuchen, den Inhalt von JSON in ein PHP-Array umzuwandeln
|
||||||
|
$blocks = json_decode($content, true);
|
||||||
|
|
||||||
|
// Wenn das JSON valide ist und Blöcke enthält
|
||||||
|
if (json_last_error() === JSON_ERROR_NONE && is_array($blocks)):
|
||||||
|
foreach ($blocks as $block):
|
||||||
|
if (isset($block['type']) && isset($block['value'])):
|
||||||
|
if ($block['type'] === 'text'): ?>
|
||||||
|
<!-- Textblock mit XSS-Schutz und Erhalt von Zeilenumbrüchen -->
|
||||||
|
<div class="article-view-body block-text">
|
||||||
|
<?php echo nl2br(htmlspecialchars($block['value'])); ?>
|
||||||
|
</div>
|
||||||
|
<?php elseif ($block['type'] === 'image'): ?>
|
||||||
|
<!-- Bildblock, der auf den relativen Pfad im uploads-Ordner verweist -->
|
||||||
|
<div class="article-view-body block-image">
|
||||||
|
<img src="<?php echo htmlspecialchars($block['value']); ?>" alt="Beitragsbild">
|
||||||
|
</div>
|
||||||
|
<?php endif;
|
||||||
|
endif;
|
||||||
|
endforeach;
|
||||||
|
else: ?>
|
||||||
|
<!-- Fallback: Wenn der Beitrag alten Reintext aus der DB enthält -->
|
||||||
|
<div class="article-view-body block-text">
|
||||||
|
<?php echo nl2br(htmlspecialchars($content)); ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Beitrags-Endbereich (Tags) -->
|
||||||
|
<?php if (isset($tags) && !empty($tags)): ?>
|
||||||
|
<div class="article-view-bottom-section">
|
||||||
|
<div class="article-view-tags-label">Tags:</div>
|
||||||
|
<div class="article-view-tags-list">
|
||||||
|
<?php
|
||||||
|
// Falls $tags ein String ist (z.B. "Web, CSS"), in ein Array umwandeln
|
||||||
|
$tagArray = is_array($tags) ? $tags : explode(',', $tags);
|
||||||
|
foreach ($tagArray as $tag):
|
||||||
|
$trimmedTag = trim($tag);
|
||||||
|
if (!empty($trimmedTag)):
|
||||||
|
?>
|
||||||
|
<span class="article-view-tag-item"><?php echo htmlspecialchars($trimmedTag); ?></span>
|
||||||
|
<?php
|
||||||
|
endif;
|
||||||
|
endforeach;
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<section class="article-comments-section">
|
||||||
|
<h2>Kommentare</h2>
|
||||||
|
|
||||||
|
<div id="comments-list">
|
||||||
|
<?php if (!empty($mainComments)): ?>
|
||||||
|
<?php foreach ($mainComments as $comment): ?>
|
||||||
|
<div class="comment-item" data-comment-id="<?php echo htmlspecialchars($comment->getId()); ?>">
|
||||||
|
<p>
|
||||||
|
<strong><?php echo htmlspecialchars($comment->getAuthor()); ?></strong>
|
||||||
|
<span><?php echo htmlspecialchars($comment->getCreated()); ?></span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><?php echo nl2br(htmlspecialchars($comment->getContent())); ?></p>
|
||||||
|
|
||||||
|
<?php if (isset($_SESSION["user_email"])): ?>
|
||||||
|
<button type="button"
|
||||||
|
class="reply-button"
|
||||||
|
data-comment-id="<?php echo htmlspecialchars($comment->getId()); ?>"
|
||||||
|
data-author="<?php echo htmlspecialchars($comment->getAuthor()); ?>">
|
||||||
|
Antworten
|
||||||
|
</button>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="comment-replies">
|
||||||
|
<?php if (isset($repliesByParent[$comment->getId()])): ?>
|
||||||
|
<?php foreach ($repliesByParent[$comment->getId()] as $reply): ?>
|
||||||
|
<div class="comment-item comment-reply">
|
||||||
|
<p>
|
||||||
|
<strong><?php echo htmlspecialchars($reply->getAuthor()); ?></strong>
|
||||||
|
<span><?php echo htmlspecialchars($reply->getCreated()); ?></span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p><?php echo nl2br(htmlspecialchars($reply->getContent())); ?></p>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<p class="no-comments-message">
|
||||||
|
Noch keine Kommentare vorhanden.
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (isset($_SESSION["user_email"])): ?>
|
||||||
|
<form id="comment-form">
|
||||||
|
<input type="hidden"
|
||||||
|
name="article_id"
|
||||||
|
value="<?php echo htmlspecialchars($_GET["id"] ?? ""); ?>">
|
||||||
|
|
||||||
|
<input type="hidden"
|
||||||
|
name="parent_comment_id"
|
||||||
|
id="parent-comment-id"
|
||||||
|
value="">
|
||||||
|
|
||||||
|
<p id="reply-info" class="reply-info" style="display: none;"></p>
|
||||||
|
|
||||||
|
<textarea name="content"
|
||||||
|
id="comment-content"
|
||||||
|
placeholder="Schreibe einen Kommentar..."
|
||||||
|
required></textarea>
|
||||||
|
|
||||||
|
<button type="submit" class="button">
|
||||||
|
Kommentar senden
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="comment-login-hint">
|
||||||
|
<p>Melde dich an, um einen Kommentar zu schreiben.</p>
|
||||||
|
<a href="index.php?pfad=login" class="button">Jetzt anmelden</a>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
include_once "php/controller/showCategory-controller.php";
|
||||||
|
?>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<?php include_once "includes/alertMessages.php"?>
|
||||||
|
|
||||||
|
<h1><?php if (isset($category) && !empty($category)){ echo htmlspecialchars($category); } ?></h1>
|
||||||
|
|
||||||
|
<div class="s-res-list">
|
||||||
|
<?php
|
||||||
|
if (!empty($articles)): ?>
|
||||||
|
<?php foreach ($articles as $article): ?>
|
||||||
|
<div class="s-res-item">
|
||||||
|
<div class="s-res-content">
|
||||||
|
<h2 class="s-res-item-title">
|
||||||
|
<a href="index.php?pfad=showArticle&id=<?php echo $article->getId(); ?>" class="s-res-link">
|
||||||
|
<?php echo htmlspecialchars($article->getTitle()); ?>
|
||||||
|
</a>
|
||||||
|
</h2>
|
||||||
|
<p class="s-res-author">Von: <span class="s-res-author-name"><?php echo htmlspecialchars($article->getAuthor()); ?></span></p>
|
||||||
|
</div>
|
||||||
|
<div class="s-res-arrow">→</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php
|
||||||
|
else: ?>
|
||||||
|
<p> Es sind noch keine Beiträge in dieser Kategorie enthalten.</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</main>
|
||||||
@@ -0,0 +1,292 @@
|
|||||||
|
<?php
|
||||||
|
$user = $user ?? null;
|
||||||
|
if (!isset($_SESSION["user"])) {
|
||||||
|
header("Location: index.php?pfad=login");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
include_once 'php/controller/showArticle-controller.php';
|
||||||
|
?>
|
||||||
|
<!--
|
||||||
|
Seite: Beitrag erstellen
|
||||||
|
Inhalt: Formular für die Erstellung eines neuen Beitrags
|
||||||
|
-->
|
||||||
|
<form method="post" action="php/controller/updateArticle-controller.php?id=<?php if(isset($id) && !empty($id)){echo htmlspecialchars($id);}else{$_SESSION["message"] = "missing_id";} ?>" id="editor-form" enctype="multipart/form-data" class="article-editor-scope.editor-container article-editor-scope editor-container">
|
||||||
|
|
||||||
|
<main class="editor-main">
|
||||||
|
<?php include_once "includes/alertMessages.php"?>
|
||||||
|
|
||||||
|
<input type="text" id="title" name="title"
|
||||||
|
value="<?php
|
||||||
|
if (isset($_SESSION['old_title']) && !empty($_SESSION['old_title'])){
|
||||||
|
echo htmlspecialchars($_SESSION['old_title']);
|
||||||
|
unset($_SESSION['old_title']);
|
||||||
|
}elseif (isset($title) && !empty($title)){
|
||||||
|
echo htmlspecialchars($title);
|
||||||
|
}
|
||||||
|
|
||||||
|
?>"
|
||||||
|
placeholder="Titel hier eingeben" required>
|
||||||
|
|
||||||
|
<!-- Hier werden die dynamischen divs via JavaScript eingefügt -->
|
||||||
|
<div id="block-container"></div>
|
||||||
|
|
||||||
|
<!-- Plus-Button und das Pop-up-Menü -->
|
||||||
|
<div id="add-block-control" class="article-editor-scope add-block-control">
|
||||||
|
<button type="button" id="plus-button" class="article-editor-scope plus-button">+</button>
|
||||||
|
<div id="block-popup" class="article-editor-scope block-popup hidden">
|
||||||
|
<button type="button" data-type="text">Textblock</button>
|
||||||
|
<button type="button" data-type="image">Bild einfügen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Unsichtbares Textfeld, das die JSON-Daten hält und an den Controller postet -->
|
||||||
|
<textarea id="content" name="content" style="display:none;"><?php
|
||||||
|
if (isset($_SESSION['old_content']) && !empty($_SESSION['old_content'])){
|
||||||
|
echo htmlspecialchars($_SESSION['old_content']);
|
||||||
|
unset($_SESSION['old_content']);
|
||||||
|
}elseif (isset($content) && !empty($content)){
|
||||||
|
echo htmlspecialchars($content);
|
||||||
|
} else {
|
||||||
|
echo '[]';
|
||||||
|
}
|
||||||
|
?></textarea>
|
||||||
|
|
||||||
|
<!-- unsichtbares Input, um die zu löschenden Bilder zu übergeben-->
|
||||||
|
<input type="hidden" id="deleted-images" name="deleted_images" value="[]">
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Seitenleiste -->
|
||||||
|
<aside class="editor-sidebar">
|
||||||
|
|
||||||
|
<div class="sidebar-block">
|
||||||
|
<button type="submit" id="editor-button" class="button">Änderungen speichern</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-block">
|
||||||
|
<label for="category">Kategorie <span class="required">*</span></label>
|
||||||
|
<select id="category" name="category" required>
|
||||||
|
<option disabled <?php
|
||||||
|
if ((!isset($_SESSION['old_category']) || empty($_SESSION['old_category'])) && (!isset($category) || empty($category))) {
|
||||||
|
echo 'selected';
|
||||||
|
}
|
||||||
|
?>>Kategorie wählen...</option>
|
||||||
|
|
||||||
|
<optgroup label="Sprachen">
|
||||||
|
<option value="deutsch" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'deutsch') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'deutsch') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Deutsch</option>
|
||||||
|
<option value="englisch" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'englisch') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'englisch') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Englisch</option>
|
||||||
|
<option value="franzoesisch" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'franzoesisch') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'franzoesisch') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Französisch</option>
|
||||||
|
<option value="latein" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'latein') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'latein') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Latein</option>
|
||||||
|
<option value="literatur" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'literatur') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'literatur') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Literatur</option>
|
||||||
|
</optgroup>
|
||||||
|
|
||||||
|
<optgroup label="MINT">
|
||||||
|
<option value="mathe" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'mathe') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'mathe') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Mathematik</option>
|
||||||
|
<option value="biologie" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'biologie') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'biologie') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Biologie</option>
|
||||||
|
<option value="chemie" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'chemie') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'chemie') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Chemie</option>
|
||||||
|
<option value="physik" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'physik') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'physik') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Physik</option>
|
||||||
|
<option value="informatik" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'informatik') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'informatik') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Informatik</option>
|
||||||
|
<option value="astronomie" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'astronomie') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'astronomie') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Astronomie</option>
|
||||||
|
</optgroup>
|
||||||
|
|
||||||
|
<optgroup label="Gesellschaft & Werte">
|
||||||
|
<option value="geschichte" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'geschichte') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'geschichte') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Geschichte</option>
|
||||||
|
<option value="erdkunde" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'erdkunde') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'erdkunde') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Erdkunde</option>
|
||||||
|
<option value="sozialkunde" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'sozialkunde') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'sozialkunde') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Sozialkunde</option>
|
||||||
|
<option value="wirtschaft" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'wirtschaft') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'wirtschaft') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Wirtschaftskunde</option>
|
||||||
|
<option value="religion" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'religion') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'religion') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Religion</option>
|
||||||
|
<option value="ethik" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'ethik') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'ethik') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Ethikunterricht</option>
|
||||||
|
<option value="philosophie" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'philosophie') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'philosophie') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Philosophie</option>
|
||||||
|
<option value="psychologie" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'psychologie') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'psychologie') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Psychologie</option>
|
||||||
|
<option value="kunst" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'kunst') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'kunst') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Kunst</option>
|
||||||
|
<option value="musik" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'musik') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'musik') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Musik</option>
|
||||||
|
<option value="theater" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'theater') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'theater') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Theater</option>
|
||||||
|
</optgroup>
|
||||||
|
|
||||||
|
<optgroup label="Technik & Praxis">
|
||||||
|
<option value="technik" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'technik') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'technik') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Technik</option>
|
||||||
|
<option value="werken" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'werken') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'werken') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Werken</option>
|
||||||
|
<option value="hauswirtschaft" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'hauswirtschaft') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'hauswirtschaft') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Hauswirtschaft</option>
|
||||||
|
<option value="sport" <?php
|
||||||
|
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||||
|
if ($_SESSION['old_category'] === 'sport') { echo 'selected'; }
|
||||||
|
} elseif (isset($category) && !empty($category)) {
|
||||||
|
if ($category === 'sport') { echo 'selected'; }
|
||||||
|
}
|
||||||
|
?>>Sport</option>
|
||||||
|
</optgroup>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
if (isset($_SESSION['old_category'])) {
|
||||||
|
unset($_SESSION['old_category']);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-block">
|
||||||
|
<label for="tags">Schlagwörter</label>
|
||||||
|
<input type="text" id="tags" name="tags"
|
||||||
|
value="<?php
|
||||||
|
if (isset($_SESSION['old_tags']) && !empty($_SESSION['old_tags'])){
|
||||||
|
echo htmlspecialchars($_SESSION['old_tags']);
|
||||||
|
unset($_SESSION['old_tags']);
|
||||||
|
} elseif (isset($tags) && !empty($tags)){
|
||||||
|
echo htmlspecialchars($tags);
|
||||||
|
}
|
||||||
|
?>"
|
||||||
|
placeholder="z.B. Technik, IT (mit Komma trennen)">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
</form>
|
||||||
@@ -0,0 +1,249 @@
|
|||||||
|
/* editor in Flexbox */
|
||||||
|
.article-editor-scope.editor-container {
|
||||||
|
display: flex;
|
||||||
|
min-height: 100vh;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||||
|
color: #1e1e1e;
|
||||||
|
background-color: #f0f2f5;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto; /* Zentriert den gesamten Container horizontal */
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-editor-scope * {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hauptbereich */
|
||||||
|
.article-editor-scope .editor-main {
|
||||||
|
flex: 1;
|
||||||
|
padding: 40px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-editor-scope #title {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-editor-scope #content {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
resize: none;
|
||||||
|
width: 100%;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Seitenleiste */
|
||||||
|
.article-editor-scope .editor-sidebar {
|
||||||
|
width: 300px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-left: 1px solid #e0e0e0;
|
||||||
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-editor-scope .sidebar-block {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-editor-scope .sidebar-block label {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-editor-scope .required {
|
||||||
|
color: #d94f4f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Formularelemente innerhalb der Editor-Sidebar */
|
||||||
|
.article-editor-scope .editor-sidebar select,
|
||||||
|
.article-editor-scope .editor-sidebar input[type="text"] {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Button-Zeugs */
|
||||||
|
.button#editor-button {
|
||||||
|
padding: 18px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Container für die dynamisch per JS eingefügten Blöcke */
|
||||||
|
.article-editor-scope #block-container {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Styling für jeden einzelnen dynamisch generierten Block */
|
||||||
|
.article-editor-scope .editor-block {
|
||||||
|
position: relative; /* Wichtig für die absolute Positionierung des Lösch-Buttons */
|
||||||
|
width: 100%;
|
||||||
|
padding: 15px;
|
||||||
|
background-color: #fafafa;
|
||||||
|
border: 1px dashed #cccccc;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Textarea innerhalb eines dynamischen Textblocks */
|
||||||
|
.article-editor-scope .editor-block textarea {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 120px;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #dddddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
resize: vertical;
|
||||||
|
outline: none;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Komfort-Löschbutton oben rechts an jedem Block */
|
||||||
|
.article-editor-scope .delete-block-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: -10px;
|
||||||
|
right: -10px;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #e74c3c;
|
||||||
|
color: #ffffff;
|
||||||
|
border: none;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-editor-scope .delete-block-btn:hover {
|
||||||
|
background-color: #c0392b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Steuerungselement für den Plus-Button und das Pop-up */
|
||||||
|
.article-editor-scope .add-block-control {
|
||||||
|
position: relative; /* Dient als Anker für das absolut positionierte Pop-up */
|
||||||
|
margin-top: 10px;
|
||||||
|
display: inline-block;
|
||||||
|
align-self: flex-start; /* Verhindert, dass der Button die volle Breite spannt */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Der runde Plus-Button */
|
||||||
|
.article-editor-scope .plus-button {
|
||||||
|
width: 45px;
|
||||||
|
height: 45px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #3498db;
|
||||||
|
color: #ffffff;
|
||||||
|
border: none;
|
||||||
|
font-size: 26px;
|
||||||
|
font-weight: 300;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
|
||||||
|
transition: background-color 0.2s ease, transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-editor-scope .plus-button:hover {
|
||||||
|
background-color: #2980b9;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Das Pop-up Menü */
|
||||||
|
.article-editor-scope .block-popup {
|
||||||
|
position: absolute;
|
||||||
|
left: 60px; /* Platziert das Menü rechts neben dem Plus-Button */
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%); /* Zentriert das Menü vertikal zum Button */
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
|
||||||
|
z-index: 999;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Die entscheidende Klasse zum Ausblenden */
|
||||||
|
.article-editor-scope .block-popup.hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons im Pop-up (Textblock / Bild einfügen) */
|
||||||
|
.article-editor-scope .block-popup button {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border: 1px solid #dcdde1;
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #2f3640;
|
||||||
|
transition: background-color 0.15s ease, border-color 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-editor-scope .block-popup button:hover {
|
||||||
|
background-color: #f1f2f6;
|
||||||
|
border-color: #b2bec3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Anpassungen unter 760px (für z.B. Smartphones) */
|
||||||
|
@media (max-width: 760px) {
|
||||||
|
.article-editor-scope.editor-container {
|
||||||
|
/* Sidebar nach unten */
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-editor-scope .editor-main {
|
||||||
|
/* weniger Innenabstand */
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-editor-scope #content {
|
||||||
|
/* Verhindert, dass das Textfeld auf kleinen Displays kollabiert */
|
||||||
|
min-height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-editor-scope .editor-sidebar {
|
||||||
|
width: 100%;
|
||||||
|
/* Entfernt den linken Rand und setzt ihn nach oben als Trenner */
|
||||||
|
border-left: none;
|
||||||
|
border-top: 1px solid #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-editor-scope .editor-block textarea {
|
||||||
|
min-height: 150px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
/* Footer‑Grundlayout */
|
/* Footer‑Grundlayout */
|
||||||
footer {
|
footer {
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: #333d43;
|
background: #333d43;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
+26
-3
@@ -3,21 +3,33 @@ main {
|
|||||||
max-width:1200px;
|
max-width:1200px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: #eef2f7;
|
background-color: #eef2f7;
|
||||||
color: #1f2937;
|
color: #1f2937;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
|
min-height: 100vh;
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: auto 1fr auto; /* Header(auto), Inhalt (bekommt Rest), Footer(auto) */
|
||||||
}
|
}
|
||||||
|
|
||||||
a, button, input, select, textarea, label, main{
|
a, button, input, select, textarea, label, main{
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
.flexbox {
|
.flexbox {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
}
|
}
|
||||||
@@ -29,6 +41,7 @@ a, button, input, select, textarea, label, main{
|
|||||||
border: 1px solid #dbe3ec;
|
border: 1px solid #dbe3ec;
|
||||||
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
||||||
transition: transform 0.2s, box-shadow 0.2s;
|
transition: transform 0.2s, box-shadow 0.2s;
|
||||||
|
flex: 1 1 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container:hover {
|
.container:hover {
|
||||||
@@ -146,7 +159,7 @@ a, button, input, select, textarea, label, main{
|
|||||||
color: #1f2937;
|
color: #1f2937;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-button {
|
.button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
background-color: #2563eb;
|
background-color: #2563eb;
|
||||||
@@ -159,7 +172,7 @@ a, button, input, select, textarea, label, main{
|
|||||||
transition: background-color 0.2s, transform 0.2s, box-shadow 0.2s;
|
transition: background-color 0.2s, transform 0.2s, box-shadow 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-button:hover {
|
.button:hover {
|
||||||
background-color: #1d4ed8;
|
background-color: #1d4ed8;
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
box-shadow: 0 4px 10px rgba(0,0,0,0.15);
|
box-shadow: 0 4px 10px rgba(0,0,0,0.15);
|
||||||
@@ -203,3 +216,13 @@ a, button, input, select, textarea, label, main{
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
box-shadow: 0 6px 20px rgba(0,0,0,0.1);
|
box-shadow: 0 6px 20px rgba(0,0,0,0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-container {
|
||||||
|
flex: 1 1 450px;
|
||||||
|
padding: 30px;
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid #dbe3ec;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 6px 20px rgba(0,0,0,0.1);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
.alert-message {
|
||||||
|
padding: 12px 16px;
|
||||||
|
margin: 12px 0;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.5;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Rote Fehlermeldung */
|
||||||
|
.alert-message.is-error {
|
||||||
|
color: #ba1a1a;
|
||||||
|
background-color: #ffeede;
|
||||||
|
border-color: #ffb4ab;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grüne Erfolgsmeldung */
|
||||||
|
.alert-message.is-success {
|
||||||
|
color: #006e2c;
|
||||||
|
background-color: #e8f5e9;
|
||||||
|
border-color: #b2dfdb;
|
||||||
|
}
|
||||||
+43
-20
@@ -57,6 +57,10 @@ CSS für die navbar
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav__dropdown-menu a:hover {
|
||||||
|
color: #1d4ed8;
|
||||||
|
}
|
||||||
|
|
||||||
.nav__link {
|
.nav__link {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -79,6 +83,8 @@ CSS für die navbar
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin-left: 0.5rem;
|
margin-left: 0.5rem;
|
||||||
|
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav__search {
|
.nav__search {
|
||||||
@@ -87,6 +93,12 @@ CSS für die navbar
|
|||||||
border-radius: 4px; /* Abgerundete Ecken */
|
border-radius: 4px; /* Abgerundete Ecken */
|
||||||
background: #fff;
|
background: #fff;
|
||||||
margin: 0 0.5rem;
|
margin: 0 0.5rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav__search input {
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav__search-button {
|
.nav__search-button {
|
||||||
@@ -113,8 +125,33 @@ CSS für die navbar
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Responsive Anpassung unter 1210px */
|
.nav__search-form {
|
||||||
@media (max-width: 1210px) {
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav__search {
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav__search-button {
|
||||||
|
display: inline-block;
|
||||||
|
background: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0.4rem 0.6rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-left: 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Anpassung unter 1240px */
|
||||||
|
@media (max-width: 1240px) {
|
||||||
.nav {
|
.nav {
|
||||||
flex-wrap: wrap; /* zweite Nav-Zeile*/
|
flex-wrap: wrap; /* zweite Nav-Zeile*/
|
||||||
padding: 0.5rem 1rem;
|
padding: 0.5rem 1rem;
|
||||||
@@ -149,8 +186,8 @@ CSS für die navbar
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Responsive Anpassungen unter 760px (für z.B. Smartphones) */
|
/* Responsive Anpassungen unter 900px (für z.B. Smartphones) */
|
||||||
@media (max-width: 760px) {
|
@media (max-width: 900px) {
|
||||||
.nav {
|
.nav {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -187,22 +224,8 @@ CSS für die navbar
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav__search {
|
.nav__search-button:hover {
|
||||||
display: flex;
|
background-color: #f8f9fa;
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav__search-button {
|
|
||||||
display: inline-block;
|
|
||||||
background: #fff;
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 0.4rem 0.6rem;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-left: 4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* restliche desktop-navbar aus */
|
/* restliche desktop-navbar aus */
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
CSS für die Profilseite
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Roter Button für Account löschen */
|
||||||
|
.delete-account-button {
|
||||||
|
background-color: #dc2626;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-account-button:hover {
|
||||||
|
background-color: #b91c1c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Beitragsliste Styling */
|
||||||
|
.section-title {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: #1f2937;
|
||||||
|
border-bottom: 2px solid #eef2f7;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.articles-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-item {
|
||||||
|
padding: 15px;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: #f9fafb;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-meta {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #6b7280;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-category {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2563eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-title {
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-tags {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 6px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
background-color: #e5e7eb;
|
||||||
|
color: #374151;
|
||||||
|
padding: 3px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bearbeiten-Button als Link deklariert */
|
||||||
|
.edit-link-button {
|
||||||
|
display: inline-block;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2563eb;
|
||||||
|
border: 1px solid #2563eb;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: background-color 0.2s, color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-link-button:hover {
|
||||||
|
background-color: #2563eb;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
@@ -0,0 +1,274 @@
|
|||||||
|
/*
|
||||||
|
CSS für die Suchergebnis-Seite
|
||||||
|
*/
|
||||||
|
/* Container und Layout-Schutz vor globalen Styles */
|
||||||
|
.s-res-layout-grid {
|
||||||
|
box-sizing: border-box;
|
||||||
|
max-width: 95%; /* Nutzt jetzt 95% der Bildschirmbreite statt starrer Pixel */
|
||||||
|
width: 95%; /* Erhöht das Limit für extrem große Monitore von 1200px auf 1600px */
|
||||||
|
margin: 2rem auto;
|
||||||
|
padding: 0 1rem;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
color: #212529;
|
||||||
|
line-height: 1.5;
|
||||||
|
|
||||||
|
/* Grid für die Zweispaltigkeit */
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 320px 1fr; /* Seitenleiste leicht verbreitert, Inhalt nimmt den gesamten Rest ein */
|
||||||
|
gap: 2.5rem; /* Etwas mehr Abstand zwischen Seitenleiste und Inhalt für das breitere Layout */
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-layout-grid * {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- SEITENLEISTE (FILTER & SUCHE) --- */
|
||||||
|
.s-res-sidebar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-sidebar-box {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-sidebar-title {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1a202c;
|
||||||
|
margin: 0 0 1rem 0;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Formular & deine Suchleisten-Klassen */
|
||||||
|
.s-res-search-form {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Filter Radio-Buttons */
|
||||||
|
.s-res-filter-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-filter-option {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
color: #4a5568;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-filter-option input[type="radio"] {
|
||||||
|
margin: 0;
|
||||||
|
accent-color: #3182ce;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- HAUPTINHALT (ERGEBNISSE) --- */
|
||||||
|
.s-res-main-content {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-header {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
border-bottom: 2px solid #dee2e6;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-main-title {
|
||||||
|
font-size: 2rem;
|
||||||
|
color: #1a202c;
|
||||||
|
margin: 0 0 0.5rem 0;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-meta {
|
||||||
|
color: #6c757d;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Einzelner Beitrag */
|
||||||
|
.s-res-item {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1.25rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-item:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
||||||
|
border-color: #cbd5e1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-content {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-item-title {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
margin: 0 0 0.25rem 0;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-link {
|
||||||
|
color: #3182ce;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-link:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #2b6cb0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-author {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: #4a5568;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-author-name {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2d3748;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-arrow {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: #a0aec0;
|
||||||
|
padding-left: 1rem;
|
||||||
|
transition: color 0.2s ease, transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-item:hover .s-res-arrow {
|
||||||
|
color: #3182ce;
|
||||||
|
transform: translateX(3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ERGEBNISSE PRO SEITE & PAGINIERUNG */
|
||||||
|
.s-res-pagination-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 2rem;
|
||||||
|
padding-top: 1.5rem;
|
||||||
|
border-top: 1px solid #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dropdown-Auswahl */
|
||||||
|
.s-res-limit-selector {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-limit-label {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #4a5568;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-limit-select {
|
||||||
|
padding: 0.35rem 1.5rem 0.35rem 0.5rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #2d3748;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 1px solid #cbd5e1;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
|
||||||
|
/* Diskreter nativer Pfeil-Style */
|
||||||
|
appearance: none;
|
||||||
|
background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://w3.org' viewBox='0 0 24 24' fill='none' stroke='%234a5568' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 0.4rem center;
|
||||||
|
background-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-limit-select:focus {
|
||||||
|
border-color: #3182ce;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Seitennummern Navigation */
|
||||||
|
.s-res-page-navigation {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-page-btn {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 1px solid #cbd5e1;
|
||||||
|
color: #4a5568;
|
||||||
|
padding: 0.35rem 0.75rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-page-btn:hover:not(:disabled) {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border-color: #cbd5e1;
|
||||||
|
color: #2d3748;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-page-btn-active {
|
||||||
|
background-color: #3182ce;
|
||||||
|
border-color: #3182ce;
|
||||||
|
color: #ffffff;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-page-btn:disabled {
|
||||||
|
background-color: #f1f5f9;
|
||||||
|
border-color: #e2e8f0;
|
||||||
|
color: #94a3b8;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-meta-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 15px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-res-likes {
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: #475569;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Anpassungen unter 760px (für z.B. Smartphones) */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.s-res-layout-grid {
|
||||||
|
grid-template-columns: 1fr; /* Stapelt Seitenleiste und Inhalt untereinander */
|
||||||
|
gap: 1.5rem;
|
||||||
|
margin: 1rem auto;
|
||||||
|
}
|
||||||
|
.s-res-pagination-footer {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,286 @@
|
|||||||
|
/* Container für den gesamten articleikel */
|
||||||
|
.article-view-container {
|
||||||
|
box-sizing: border-box;
|
||||||
|
max-width: 900px; /* Angenehme Lesebreite für längere Texte */
|
||||||
|
margin: 3rem auto;
|
||||||
|
padding: 0 2rem;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
color: #212529;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-view-container * {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- KOPFDATEN-BEREICH --- */
|
||||||
|
.article-view-top-section {
|
||||||
|
margin-bottom: 2.5rem;
|
||||||
|
border-bottom: 1px solid #e2e8f0;
|
||||||
|
padding-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Kategorie-Badge */
|
||||||
|
.article-view-category {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #ebf8ff;
|
||||||
|
color: #2b6cb0;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
padding: 0.25rem 0.75rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hauptüberschrift */
|
||||||
|
.article-view-title {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
color: #1a202c;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin: 0 0 0.75rem 0;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Autoren-Zeile */
|
||||||
|
.article-view-meta {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
color: #4a5568;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-view-author strong {
|
||||||
|
color: #2d3748;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- INHALT --- */
|
||||||
|
.article-view-content {
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-view-body {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
color: #2d3748;
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- TAG-BEREICH --- */
|
||||||
|
.article-view-bottom-section {
|
||||||
|
border-top: 1px solid #e2e8f0;
|
||||||
|
padding-top: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-view-tags-label {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #718096;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-view-tags-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Einzelner Tag */
|
||||||
|
.article-view-tag-item {
|
||||||
|
background-color: #f1f5f9;
|
||||||
|
color: #475569;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 0.35rem 0.75rem;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
transition: background-color 0.2s, color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-view-tag-item:hover {
|
||||||
|
background-color: #e2e8f0;
|
||||||
|
color: #1e293b;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-view-content {
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.5rem; /* Erzeugt einen sauberen Abstand zwischen den einzelnen Blöcken */
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-view-body {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
color: #2d3748;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-view-body.block-text {
|
||||||
|
white-space: pre-line;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-view-body.block-image {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center; /* Zentriert das Bild horizontal */
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-view-body.block-image img {
|
||||||
|
max-width: 100%; /* Verhindert das Ausbrechen aus der Lesebreite */
|
||||||
|
height: auto; /* Behält das originale Seitenverhältnis bei */
|
||||||
|
border-radius: 6px; /* Optionale leichte Rundung für ein moderneres Layout */
|
||||||
|
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.03); /* Minimaler, eleganter Schatten */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Anpassungen unter 760px (für z.B. Smarticlephones) */
|
||||||
|
@media (max-width: 760px) {
|
||||||
|
.article-view-container {
|
||||||
|
margin: 1.5rem auto;
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-view-title {
|
||||||
|
font-size: 1.85rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- KOMMENTARE --- */
|
||||||
|
|
||||||
|
.article-comments-section {
|
||||||
|
margin-top: 3rem;
|
||||||
|
padding-top: 2rem;
|
||||||
|
border-top: 1px solid #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-comments-section h2 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#comments-list {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-item {
|
||||||
|
background-color: #f8fafc;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 1rem 1.25rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#comment-form {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#comment-content {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 130px;
|
||||||
|
padding: 1rem;
|
||||||
|
border: 1px solid #cbd5e1;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-family: inherit;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
#comment-form .button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reply-button {
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #2563eb;
|
||||||
|
font-weight: 700;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reply-button:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-replies {
|
||||||
|
margin-top: 1rem;
|
||||||
|
margin-left: 2rem;
|
||||||
|
padding-left: 1rem;
|
||||||
|
border-left: 3px solid #cbd5e1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-reply {
|
||||||
|
background-color: #eef6ff;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reply-info {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
color: #475569;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.comment-login-hint {
|
||||||
|
margin-top: 2rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
background-color: #f8fafc;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-login-hint p {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Like-Button etc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.category-and-likes-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-view-likes {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
font-size: 0.95em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-view-likes .like-count {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-view-likes .login-hint {
|
||||||
|
font-size: 0.8em;
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interaktiver Like/Unlike-Button */
|
||||||
|
.like-toggle-btn {
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 4px 10px;
|
||||||
|
border: 1px solid #bbb;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: background-color 0.2s ease, border-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.like-toggle-btn:hover {
|
||||||
|
background-color: #eaeaea;
|
||||||
|
border-color: #999;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
<!--
|
|
||||||
Artikel: Datenschutz vs Datensicherheit
|
|
||||||
Funktion: Inhalt zum Fach Informatik
|
|
||||||
-->
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="de">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="description" content="EduForge">
|
|
||||||
<meta name="author" content="Niklas Ortmann">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<link rel="icon" type="image/x-icon" href="images/logos/logo_icon.ico">
|
|
||||||
<link rel="stylesheet" href="css/main.css">
|
|
||||||
<link rel="stylesheet" href="css/navbar.css">
|
|
||||||
<link rel="stylesheet" href="css/footer.css">
|
|
||||||
<title>EduForge</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<?php
|
|
||||||
include_once 'includes/navbar.php';
|
|
||||||
?>
|
|
||||||
<main>
|
|
||||||
|
|
||||||
<h1>Datenschutz vs Datensicherheit</h1>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Datenschutz ist in unserer digital vernetzten Welt allgegenwärtig – ob in sozialen Netzwerken, beim
|
|
||||||
Online-Shopping oder dem Einsatz von Bonuskarten, bei der Auswertung von Konsumverhalten und selbstverständlich
|
|
||||||
auch im schulischen Kontext.
|
|
||||||
</p>
|
|
||||||
<h2>
|
|
||||||
Was ist Datenschutz?
|
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
Datenschutz bedeutet, dass persönliche Daten nur gesammelt, gespeichert und verwendet werden dürfen, wenn
|
|
||||||
es wirklich nötig ist – und nur für klar festgelegte Zwecke. Es geht darum, die Privatsphäre von Menschen
|
|
||||||
zu schützen.
|
|
||||||
</p>
|
|
||||||
<h2>
|
|
||||||
Was ist Datenschutz?
|
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
Datensicherheit umfasst alle technischen und organisatorischen Maßnahmen, die verhindern sollen, dass Daten
|
|
||||||
verloren gehen, beschädigt oder unbemerkt verändert werden. Sie schützt Informationen vor Missbrauch.
|
|
||||||
</p>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
include_once 'includes/footer.php';
|
|
||||||
?>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<FilesMatch "\.db">
|
||||||
|
deny from all
|
||||||
|
</FilesMatch>
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "internal_error"): ?>
|
||||||
|
<p class="alert-message is-error">
|
||||||
|
Es ist ein interner Fehler beim Speichern aufgetreten. Bitte versuche es erneut.
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "missing_parameters"): ?>
|
||||||
|
<p class="alert-message is-error">
|
||||||
|
Jeder Beitrag muss einen Titel, Kategorie und Inhalt besitzen.
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "invalid_title"): ?>
|
||||||
|
<p class="alert-message is-error">
|
||||||
|
Der Titel enthält ungültige Zeichen oder erfüllt die Länge von 5-120 Zeichen nicht.
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "invalid_content"): ?>
|
||||||
|
<p class="alert-message is-error">
|
||||||
|
Ein Beitrag muss Inhalt besitzen. Text- und Bildelemente dürfen nicht leer sein!
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "invalid_category"): ?>
|
||||||
|
<p class="alert-message is-error">
|
||||||
|
Die ausgewählte Kategorie ist ungültig.
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "invalid_tags"): ?>
|
||||||
|
<p class="alert-message is-error">
|
||||||
|
Ungültige Schlagworte gefunden. Erlaubt sind nur Buchstaben, Zahlen, Leerzeichen und Bindestriche (2-50 Zeichen).
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "validation_missing"): ?>
|
||||||
|
<p class="alert-message is-error">
|
||||||
|
Bei der Validierung deiner Daten ist ein Fehler aufgetreten. Bitte versuche es erneut.
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "invalid_category"): ?>
|
||||||
|
<p class="alert-message is-error">
|
||||||
|
Diese Kategorie ist nicht gültig.
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "missing_id"): ?>
|
||||||
|
<p class="alert-message is-error">
|
||||||
|
Die Artikel-ID fehlt oder ist ungültig.
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "not_found_article"): ?>
|
||||||
|
<p class="alert-message is-error">
|
||||||
|
Der angeforderte Beitrag konnte nicht gefunden werden.
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "unauthorized_access"): ?>
|
||||||
|
<p class="alert-message is-error">
|
||||||
|
Du hast keine Berechtigung, diesen Beitrag zu bearbeiten oder zu löschen.
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "article_deleted"): ?>
|
||||||
|
<p class="alert-message is-success">
|
||||||
|
Der Beitrag wurde erfolgreich gelöscht.
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "new_article"): ?>
|
||||||
|
<p class="alert-message is-success">
|
||||||
|
Dein Beitrag wurde erfolgreich veröffentlicht!
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "article_updated"): ?>
|
||||||
|
<p class="alert-message is-success">
|
||||||
|
Der Beitrag wurde erfolgreich bearbeitet und gespeichert.
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "profile_updated"): ?>
|
||||||
|
<p class="alert-message is-success">
|
||||||
|
Das Profil wurde erfolgreich bearbeitet.
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "image_upload_error"): ?>
|
||||||
|
<p class="alert-message is-error">
|
||||||
|
Das Bild konnte nicht hochgeladen werden. Bitte versuche es erneut oder verwende ein anderes Bildformat.
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php
|
||||||
|
unset($_SESSION["message"]);
|
||||||
|
?>
|
||||||
+3
-3
@@ -1,5 +1,5 @@
|
|||||||
<footer>
|
<footer>
|
||||||
<a href="impressum.php">Impressum</a>
|
<a href="index.php?pfad=impressum">Impressum</a>
|
||||||
<a href="datenschutz.php">Datenschutz</a>
|
<a href="index.php?pfad=datenschutz">Datenschutz</a>
|
||||||
<a href="nutzungsbedingungen.php">Nutzungsbedingungen</a>
|
<a href="index.php?pfad=nutzungsbedingungen">Nutzungsbedingungen</a>
|
||||||
</footer>
|
</footer>
|
||||||
+113
-66
@@ -1,73 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Globales Menü, wird via PHP später in alle Seiten eingebunden
|
Globales Menü, wird via PHP später in alle Seiten eingebunden
|
||||||
-->
|
-->
|
||||||
<nav class="nav">
|
<nav class="nav">
|
||||||
<div class="nav__left">
|
<div class="nav__left">
|
||||||
<a href="index.php" class="nav__logo">
|
<a href="index.php" class="nav__logo">
|
||||||
<img src="images/logos/logo_full.png" alt="Logo">
|
<img src="images/logos/logo_full.png" alt="Logo">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Mobile navbar mit Burger-Symbol -->
|
<!-- Mobile navbar mit Burger-Symbol -->
|
||||||
<input type="checkbox" id="nav-toggle" class="nav__checkbox">
|
<input type="checkbox" id="nav-toggle" class="nav__checkbox">
|
||||||
<label for="nav-toggle" id="mobile-nav">
|
<label for="nav-toggle" id="mobile-nav">
|
||||||
<span>☰</span> <!-- Burger Icon Symbol -->
|
<span>☰</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<ul id="mobile-nav" class="nav__mobile-menu">
|
<ul id="mobile-nav" class="nav__mobile-menu">
|
||||||
<li>
|
<li>
|
||||||
<label for="nav-toggle" class="nav__close-btn">×</label>
|
<label for="nav-toggle" class="nav__close-btn">×</label>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="index.php?pfad=profile">Profil</a></li>
|
|
||||||
<li><a href="index.php?pfad=login">Anmelden</a></li>
|
<?php if (isset($_SESSION['user'])): ?>
|
||||||
<li><a href="index.php?pfad=register">Registrieren</a></li>
|
|
||||||
|
<li><a href="index.php?pfad=profile">Profil</a></li>
|
||||||
|
<li><a href="index.php?pfad=logout">Abmelden</a></li>
|
||||||
|
<li><a href="index.php?pfad=createArticle">Beitrag erstellen</a></li>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<li><a href="index.php?pfad=login">Anmelden</a></li>
|
||||||
|
<li><a href="index.php?pfad=register">Registrieren</a></li>
|
||||||
|
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<label class="nav__mobile-label">Sprachen</label>
|
<label class="nav__mobile-label">Sprachen</label>
|
||||||
<ul class="nav__mobile-submenu">
|
<ul class="nav__mobile-submenu">
|
||||||
<li><a href="index.php?pfad=deutsch">Deutsch</a></li>
|
<li><a href="index.php?pfad=showCategory&category=deutsch">Deutsch</a></li>
|
||||||
<li><a href="index.php?pfad=englisch">Englisch</a></li>
|
<li><a href="index.php?pfad=showCategory&category=englisch">Englisch</a></li>
|
||||||
<li><a href="index.php?pfad=franzoesisch">Französisch</a></li>
|
<li><a href="index.php?pfad=showCategory&category=franzoesisch">Französisch</a></li>
|
||||||
<li><a href="index.php?pfad=latein">Latein</a></li>
|
<li><a href="index.php?pfad=showCategory&category=latein">Latein</a></li>
|
||||||
<li><a href="index.php?pfad=literatur">Literatur</a></li>
|
<li><a href="index.php?pfad=showCategory&category=literatur">Literatur</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<label class="nav__mobile-label">MINT</label>
|
<label class="nav__mobile-label">MINT</label>
|
||||||
<ul class="nav__mobile-submenu">
|
<ul class="nav__mobile-submenu">
|
||||||
<li><a href="index.php?pfad=mathe">Mathematik</a></li>
|
<li><a href="index.php?pfad=showCategory&category=mathe">Mathematik</a></li>
|
||||||
<li><a href="index.php?pfad=biologie">Biologie</a></li>
|
<li><a href="index.php?pfad=showCategory&category=biologie">Biologie</a></li>
|
||||||
<li><a href="index.php?pfad=chemie">Chemie</a></li>
|
<li><a href="index.php?pfad=showCategory&category=chemie">Chemie</a></li>
|
||||||
<li><a href="index.php?pfad=englisch">Physik</a></li>
|
<li><a href="index.php?pfad=showCategory&category=physik">Physik</a></li>
|
||||||
<li><a href="index.php?pfad=informatik">Informatik</a></li>
|
<li><a href="index.php?pfad=showCategory&category=informatik">Informatik</a></li>
|
||||||
<li><a href="index.php?pfad=astronomie">Astronomie</a></li>
|
<li><a href="index.php?pfad=showCategory&category=astronomie">Astronomie</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<label class="nav__mobile-label">Gesellschaft & Werte</label>
|
<label class="nav__mobile-label">Gesellschaft & Werte</label>
|
||||||
<ul class="nav__mobile-submenu">
|
<ul class="nav__mobile-submenu">
|
||||||
<li><a href="index.php?pfad=geschichte">Geschichte</a></li>
|
<li><a href="index.php?pfad=showCategory&category=geschichte">Geschichte</a></li>
|
||||||
<li><a href="index.php?pfad=erdkunde">Erdkunde</a></li>
|
<li><a href="index.php?pfad=showCategory&category=erdkunde">Erdkunde</a></li>
|
||||||
<li><a href="index.php?pfad=sozialkunde">Sozialkunde</a></li>
|
<li><a href="index.php?pfad=showCategory&category=sozialkunde">Sozialkunde</a></li>
|
||||||
<li><a href="index.php?pfad=wirtschaft">Wirtschaftskunde</a></li>
|
<li><a href="index.php?pfad=showCategory&category=wirtschaft">Wirtschaftskunde</a></li>
|
||||||
<li><a href="index.php?pfad=religion">Religion</a></li>
|
<li><a href="index.php?pfad=showCategory&category=religion">Religion</a></li>
|
||||||
<li><a href="index.php?pfad=ethik">Ethikunterricht</a></li>
|
<li><a href="index.php?pfad=showCategory&category=ethik">Ethikunterricht</a></li>
|
||||||
<li><a href="index.php?pfad=philosophie">Philosophie</a></li>
|
<li><a href="index.php?pfad=showCategory&category=philosophie">Philosophie</a></li>
|
||||||
<li><a href="index.php?pfad=psychologie">Psychologie</a></li>
|
<li><a href="index.php?pfad=showCategory&category=psychologie">Psychologie</a></li>
|
||||||
<li><a href="index.php?pfad=kunst">Kunst</a></li>
|
<li><a href="index.php?pfad=showCategory&category=kunst">Kunst</a></li>
|
||||||
<li><a href="index.php?pfad=musik">Musik</a></li>
|
<li><a href="index.php?pfad=showCategory&category=musik">Musik</a></li>
|
||||||
<li><a href="index.php?pfad=theater">Theater</a></li>
|
<li><a href="index.php?pfad=showCategory&category=theater">Theater</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<label class="nav__mobile-label">Technik & Praxis</label>
|
<label class="nav__mobile-label">Technik & Praxis</label>
|
||||||
<ul class="nav__mobile-submenu">
|
<ul class="nav__mobile-submenu">
|
||||||
<li><a href="index.php?pfad=technik">Technik</a></li>
|
<li><a href="index.php?pfad=showCategory&category=technik">Technik</a></li>
|
||||||
<li><a href="index.php?pfad=werken">Werken</a></li>
|
<li><a href="index.php?pfad=showCategory&category=werken">Werken</a></li>
|
||||||
<li><a href="index.php?pfad=hauswirtschaft">Hauswirtschaft</a></li>
|
<li><a href="index.php?pfad=showCategory&category=hauswirtschaft">Hauswirtschaft</a></li>
|
||||||
<li><a href="index.php?pfad=sport">Sport</a></li>
|
<li><a href="index.php?pfad=showCategory&category=sport">Sport</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -77,59 +94,89 @@ Globales Menü, wird via PHP später in alle Seiten eingebunden
|
|||||||
<div class="nav__item nav__dropdown">
|
<div class="nav__item nav__dropdown">
|
||||||
<button class="nav__dropdown-toggle">Sprachen</button>
|
<button class="nav__dropdown-toggle">Sprachen</button>
|
||||||
<div class="nav__dropdown-menu">
|
<div class="nav__dropdown-menu">
|
||||||
<a href="index.php?pfad=deutsch">Deutsch</a>
|
<a href="index.php?pfad=showCategory&category=deutsch">Deutsch</a>
|
||||||
<a href="index.php?pfad=englisch">Englisch</a>
|
<a href="index.php?pfad=showCategory&category=englisch">Englisch</a>
|
||||||
<a href="index.php?pfad=franzoesisch">Französisch</a>
|
<a href="index.php?pfad=showCategory&category=franzoesisch">Französisch</a>
|
||||||
<a href="index.php?pfad=latein">Latein</a>
|
<a href="index.php?pfad=showCategory&category=latein">Latein</a>
|
||||||
<a href="index.php?pfad=literatur">Literatur</a>
|
<a href="index.php?pfad=showCategory&category=literatur">Literatur</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="nav__item nav__dropdown">
|
<div class="nav__item nav__dropdown">
|
||||||
<button class="nav__dropdown-toggle">MINT</button>
|
<button class="nav__dropdown-toggle">MINT</button>
|
||||||
<div class="nav__dropdown-menu">
|
<div class="nav__dropdown-menu">
|
||||||
<a href="index.php?pfad=mathe">Mathematik</a>
|
<a href="index.php?pfad=showCategory&category=mathe">Mathematik</a>
|
||||||
<a href="index.php?pfad=biologie">Biologie</a>
|
<a href="index.php?pfad=showCategory&category=biologie">Biologie</a>
|
||||||
<a href="index.php?pfad=chemie">Chemie</a>
|
<a href="index.php?pfad=showCategory&category=chemie">Chemie</a>
|
||||||
<a href="index.php?pfad=physik">Physik</a>
|
<a href="index.php?pfad=showCategory&category=physik">Physik</a>
|
||||||
<a href="index.php?pfad=informatik">Informatik</a>
|
<a href="index.php?pfad=showCategory&category=informatik">Informatik</a>
|
||||||
<a href="index.php?pfad=astronomie">Astronomie</a>
|
<a href="index.php?pfad=showCategory&category=astronomie">Astronomie</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="nav__item nav__dropdown">
|
<div class="nav__item nav__dropdown">
|
||||||
<button class="nav__dropdown-toggle">Gesellschaft & Werte</button>
|
<button class="nav__dropdown-toggle">Gesellschaft & Werte</button>
|
||||||
<div class="nav__dropdown-menu">
|
<div class="nav__dropdown-menu">
|
||||||
<a href="index.php?pfad=geschichte">Geschichte</a>
|
<a href="index.php?pfad=showCategory&category=geschichte">Geschichte</a>
|
||||||
<a href="index.php?pfad=erdkunde">Erdkunde</a>
|
<a href="index.php?pfad=showCategory&category=erdkunde">Erdkunde</a>
|
||||||
<a href="index.php?pfad=sozialkunde">Sozialkunde</a>
|
<a href="index.php?pfad=showCategory&category=sozialkunde">Sozialkunde</a>
|
||||||
<a href="index.php?pfad=wirtschaft">Wirtschaftskunde</a>
|
<a href="index.php?pfad=showCategory&category=wirtschaft">Wirtschaftskunde</a>
|
||||||
<a href="index.php?pfad=religion">Religion</a>
|
<a href="index.php?pfad=showCategory&category=religion">Religion</a>
|
||||||
<a href="index.php?pfad=ethik">Ethikunterricht</a>
|
<a href="index.php?pfad=showCategory&category=ethik">Ethikunterricht</a>
|
||||||
<a href="index.php?pfad=philosophie">Philosophie</a>
|
<a href="index.php?pfad=showCategory&category=philosophie">Philosophie</a>
|
||||||
<a href="index.php?pfad=psychologie">Psychologie</a>
|
<a href="index.php?pfad=showCategory&category=psychologie">Psychologie</a>
|
||||||
<a href="index.php?pfad=kunst">Kunst</a>
|
<a href="index.php?pfad=showCategory&category=kunst">Kunst</a>
|
||||||
<a href="index.php?pfad=musik">Musik</a>
|
<a href="index.php?pfad=showCategory&category=musik">Musik</a>
|
||||||
<a href="index.php?pfad=theater">Theater</a>
|
<a href="index.php?pfad=showCategory&category=theater">Theater</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="nav__item nav__dropdown">
|
<div class="nav__item nav__dropdown">
|
||||||
<button class="nav__dropdown-toggle">Technik & Praxis</button>
|
<button class="nav__dropdown-toggle">Technik & Praxis</button>
|
||||||
<div class="nav__dropdown-menu">
|
<div class="nav__dropdown-menu">
|
||||||
<a href="index.php?pfad=technik">Technik</a>
|
<a href="index.php?pfad=showCategory&category=technik">Technik</a>
|
||||||
<a href="index.php?pfad=werken">Werken</a>
|
<a href="index.php?pfad=showCategory&category=werken">Werken</a>
|
||||||
<a href="index.php?pfad=hauswirtschaft">Hauswirtschaft</a>
|
<a href="index.php?pfad=showCategory&category=hauswirtschaft">Hauswirtschaft</a>
|
||||||
<a href="index.php?pfad=sport">Sport</a>
|
<a href="index.php?pfad=showCategory&category=sport">Sport</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="nav__right">
|
<div class="nav__right">
|
||||||
<a href="index.php?pfad=profile" class="nav_item nav__link">Profil</a>
|
|
||||||
<div class="nav__search">
|
<?php if (isset($_SESSION['user'])): ?>
|
||||||
<?php
|
|
||||||
include_once 'search.php';
|
<a href="index.php?pfad=profile" class="nav__item nav__link">
|
||||||
?>
|
Profil
|
||||||
</div>
|
</a>
|
||||||
<a href = "index.php?pfad=login" class="nav__item nav__button">Anmelden</a>
|
|
||||||
<a href = "index.php?pfad=register" class="nav__item nav__button">Registrieren</a>
|
<div class="nav__search">
|
||||||
|
<?php include_once 'search.php'; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a href="index.php?pfad=createArticle" class="nav__item nav__button">
|
||||||
|
Beitrag erstellen
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="index.php?pfad=logout" class="nav__item nav__button">
|
||||||
|
Abmelden
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
|
<div class="nav__search">
|
||||||
|
<?php include_once 'search.php'; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a href="index.php?pfad=login" class="nav__item nav__button">
|
||||||
|
Anmelden
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="index.php?pfad=register" class="nav__item nav__button">
|
||||||
|
Registrieren
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
+7
-2
@@ -2,5 +2,10 @@
|
|||||||
Suchleiste. Wird via PHP später in alle Seiten eingebunden
|
Suchleiste. Wird via PHP später in alle Seiten eingebunden
|
||||||
-->
|
-->
|
||||||
<!--<label for="site-search">Suche</label>-->
|
<!--<label for="site-search">Suche</label>-->
|
||||||
<input type="search" id="site-search" name="q" placeholder="Suchen..." class="nav__search">
|
<form action="php/controller/search-results-controller.php" method="GET" class="nav__search-form">
|
||||||
<button type="submit" class="nav__search-button">Suchen</button>
|
|
||||||
|
<input type="hidden" name="pfad" value="search-results">
|
||||||
|
|
||||||
|
<input type="search" id="site-search" name="q" placeholder="Suchen..." class="nav__search" maxlength="50" required>
|
||||||
|
<button type="submit" class="nav__search-button">Suchen</button>
|
||||||
|
</form>
|
||||||
|
|||||||
@@ -1,39 +1,87 @@
|
|||||||
<!--
|
<?php
|
||||||
Seite: Index der Lernplattform
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
Funktion: Webseitengerüst, Anzeigen von Content
|
session_start();
|
||||||
-->
|
}
|
||||||
<!DOCTYPE html>
|
ob_start();
|
||||||
<html lang="de">
|
|
||||||
|
|
||||||
<head>
|
$pfad = $_GET["pfad"] ?? "home";
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="description" content="EduForge">
|
|
||||||
<meta name="author" content="Niklas Ortmann">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<link rel="icon" type="image/x-icon" href="images/logos/logo_icon.ico">
|
|
||||||
<link rel="stylesheet" href="css/main.css">
|
|
||||||
<link rel="stylesheet" href="css/navbar.css">
|
|
||||||
<link rel="stylesheet" href="css/footer.css">
|
|
||||||
<title>EduForge</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Controller für Aktionen werden vor der HTML-Ausgabe geladen,
|
||||||
|
damit Weiterleitungen mit header() funktionieren.
|
||||||
|
*/
|
||||||
|
if ($pfad === "login") {
|
||||||
|
include_once "php/controller/login-controller.php";
|
||||||
|
}
|
||||||
|
|
||||||
<body>
|
if ($pfad === "register") {
|
||||||
|
include_once "php/controller/register-controller.php";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pfad === "logout") {
|
||||||
|
include_once "php/controller/logout-controller.php";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pfad === "deleteAccount") {
|
||||||
|
include_once "php/controller/deleteAccount-controller.php";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Seite: Index der Lernplattform
|
||||||
|
Funktion: Webseitengerüst, Anzeigen von Content
|
||||||
|
-->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="description" content="EduForge">
|
||||||
|
<meta name="author" content="Niklas Ortmann">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="icon" type="image/x-icon" href="images/logos/logo_icon.ico">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="css/main.css">
|
||||||
|
<link rel="stylesheet" href="css/navbar.css">
|
||||||
|
<link rel="stylesheet" href="css/footer.css">
|
||||||
|
<link rel="stylesheet" href="css/search-results.css">
|
||||||
|
<link rel="stylesheet" href="css/createArticle.css">
|
||||||
|
<link rel="stylesheet" href="css/profile.css">
|
||||||
|
<link rel="stylesheet" href="css/showArticle.css">
|
||||||
|
<link rel="stylesheet" href="css/message.css">
|
||||||
|
|
||||||
|
<script src="js/paginator.js" async></script>
|
||||||
|
<script src="js/sorter.js" async></script>
|
||||||
|
<script src="js/comments.js" defer></script>
|
||||||
|
<script src="js/editor.js" async></script>
|
||||||
|
<script src="js/filter.js" async></script>
|
||||||
|
|
||||||
|
<title>EduForge</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
include_once 'includes/navbar.php';
|
include_once 'includes/navbar.php';
|
||||||
//Dynamischer Inhalt:
|
|
||||||
if (isset($_GET["pfad"])) {
|
/*
|
||||||
if (file_exists('content/' . $_GET["pfad"] . '.php')) {
|
Dynamischer Inhalt:
|
||||||
include_once 'content/' . $_GET["pfad"] . '.php';
|
Je nach pfad-Parameter wird die passende Datei aus content geladen.
|
||||||
} else {
|
*/
|
||||||
include_once 'content/404.php';
|
if (file_exists('content/' . $pfad . '.php')) {
|
||||||
}
|
include_once 'content/' . $pfad . '.php';
|
||||||
} else {
|
} else {
|
||||||
include_once 'content/home.php';
|
include_once 'content/404.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
include_once 'includes/footer.php';
|
include_once 'includes/footer.php';
|
||||||
?>
|
?>
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
ob_end_flush();
|
||||||
|
?>
|
||||||
+137
@@ -0,0 +1,137 @@
|
|||||||
|
/**
|
||||||
|
* Initialisiert die Kommentarfunktion.
|
||||||
|
*
|
||||||
|
* Kommentare werden per AJAX gespeichert,
|
||||||
|
* ohne dass die Seite neu geladen werden muss.
|
||||||
|
*/
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
const form = document.getElementById("comment-form");
|
||||||
|
const commentsList = document.getElementById("comments-list");
|
||||||
|
const commentContent = document.getElementById("comment-content");
|
||||||
|
const parentCommentInput = document.getElementById("parent-comment-id");
|
||||||
|
const replyInfo = document.getElementById("reply-info");
|
||||||
|
|
||||||
|
if (!form || !commentsList || !commentContent || !parentCommentInput || !replyInfo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aktiviert einen einzelnen Antworten-Button.
|
||||||
|
*
|
||||||
|
* @param {HTMLButtonElement} button Antworten-Button
|
||||||
|
*/
|
||||||
|
function registerReplyButton(button) {
|
||||||
|
button.addEventListener("click", function () {
|
||||||
|
parentCommentInput.value = button.dataset.commentId;
|
||||||
|
replyInfo.textContent = "Antwort auf " + button.dataset.author;
|
||||||
|
replyInfo.style.display = "block";
|
||||||
|
commentContent.focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registriert alle bereits vorhandenen Antwort-Buttons.
|
||||||
|
*/
|
||||||
|
document.querySelectorAll(".reply-button").forEach(function (button) {
|
||||||
|
registerReplyButton(button);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sendet Kommentare per AJAX an den Server.
|
||||||
|
*/
|
||||||
|
form.addEventListener("submit", function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const formData = new FormData(form);
|
||||||
|
const parentCommentId = parentCommentInput.value;
|
||||||
|
|
||||||
|
fetch("php/ajax/add-comment.php", {
|
||||||
|
method: "POST",
|
||||||
|
body: formData
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (!data.success) {
|
||||||
|
alert(data.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emptyMessage = commentsList.querySelector(".no-comments-message");
|
||||||
|
|
||||||
|
if (emptyMessage) {
|
||||||
|
emptyMessage.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
const commentElement = document.createElement("div");
|
||||||
|
commentElement.classList.add("comment-item");
|
||||||
|
|
||||||
|
if (parentCommentId) {
|
||||||
|
commentElement.classList.add("comment-reply");
|
||||||
|
|
||||||
|
commentElement.innerHTML = `
|
||||||
|
<p>
|
||||||
|
<strong>${escapeHtml(data.author)}</strong>
|
||||||
|
<span>${escapeHtml(data.created)}</span>
|
||||||
|
</p>
|
||||||
|
<p>${escapeHtml(data.content).replace(/\n/g, "<br>")}</p>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const parentReplies = document.querySelector(
|
||||||
|
`.comment-item[data-comment-id="${parentCommentId}"] .comment-replies`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (parentReplies) {
|
||||||
|
parentReplies.appendChild(commentElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
commentElement.dataset.commentId = data.commentId;
|
||||||
|
|
||||||
|
commentElement.innerHTML = `
|
||||||
|
<p>
|
||||||
|
<strong>${escapeHtml(data.author)}</strong>
|
||||||
|
<span>${escapeHtml(data.created)}</span>
|
||||||
|
</p>
|
||||||
|
<p>${escapeHtml(data.content).replace(/\n/g, "<br>")}</p>
|
||||||
|
|
||||||
|
<button type="button"
|
||||||
|
class="reply-button"
|
||||||
|
data-comment-id="${escapeHtml(data.commentId)}"
|
||||||
|
data-author="${escapeHtml(data.author)}">
|
||||||
|
Antworten
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="comment-replies"></div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
commentsList.prepend(commentElement);
|
||||||
|
|
||||||
|
const newReplyButton = commentElement.querySelector(".reply-button");
|
||||||
|
|
||||||
|
if (newReplyButton) {
|
||||||
|
registerReplyButton(newReplyButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commentContent.value = "";
|
||||||
|
parentCommentInput.value = "";
|
||||||
|
replyInfo.textContent = "";
|
||||||
|
replyInfo.style.display = "none";
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
alert("Kommentar konnte nicht gesendet werden.");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entfernt HTML-Sonderzeichen aus Nutzereingaben.
|
||||||
|
*
|
||||||
|
* @param {string} text Zu bereinigender Text
|
||||||
|
* @returns {string} Sicherer Text
|
||||||
|
*/
|
||||||
|
function escapeHtml(text) {
|
||||||
|
const div = document.createElement("div");
|
||||||
|
div.textContent = text;
|
||||||
|
return div.innerHTML;
|
||||||
|
}
|
||||||
|
});
|
||||||
+179
@@ -0,0 +1,179 @@
|
|||||||
|
console.log("Die JavaScript-Datei wurde erfolgreich geladen!");
|
||||||
|
|
||||||
|
function initEditor() {
|
||||||
|
const form = document.getElementById("editor-form");
|
||||||
|
|
||||||
|
if (!form) {
|
||||||
|
console.error("Skript abgebrochen: Formular nicht gefunden!");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
console.log("Formular gefunden und Editor initialisiert:", form);
|
||||||
|
}
|
||||||
|
|
||||||
|
const container = document.getElementById("block-container");
|
||||||
|
const plusButton = document.getElementById("plus-button");
|
||||||
|
const popup = document.getElementById("block-popup");
|
||||||
|
const hiddenContentInput = document.getElementById("content");
|
||||||
|
|
||||||
|
const initialImages = [];
|
||||||
|
|
||||||
|
// Pop-up umschalten bei Klick auf das Plus
|
||||||
|
plusButton.addEventListener("click", () => {
|
||||||
|
popup.classList.toggle("hidden");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Klick auf eine Block-Option im Pop-up
|
||||||
|
popup.querySelectorAll("button").forEach(btn => {
|
||||||
|
btn.addEventListener("click", function() {
|
||||||
|
const type = this.getAttribute("data-type");
|
||||||
|
addBlockElement(type, "");
|
||||||
|
popup.classList.add("hidden");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Erstellt ein visuelles HTML-Element im Editor
|
||||||
|
function addBlockElement(type, value = "") {
|
||||||
|
const blockDiv = document.createElement("div");
|
||||||
|
blockDiv.classList.add("editor-block");
|
||||||
|
blockDiv.setAttribute("data-type", type);
|
||||||
|
|
||||||
|
// Wenn es ein existierendes Server-Bild beim Laden ist, Pfad im globalen Array sichern
|
||||||
|
if (type === "image" && value && typeof value === 'string' && value.startsWith('uploads/')) {
|
||||||
|
initialImages.push(value);
|
||||||
|
blockDiv.setAttribute("data-value", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Löschen-Button
|
||||||
|
const deleteBtn = document.createElement("button");
|
||||||
|
deleteBtn.type = "button";
|
||||||
|
deleteBtn.innerHTML = "✕";
|
||||||
|
deleteBtn.classList.add("delete-block-btn");
|
||||||
|
deleteBtn.addEventListener("click", () => {
|
||||||
|
// ANPASSUNG 2B: Logik hier komplett geleert. Das '✕' entfernt den Block jetzt nur noch sicher aus dem HTML.
|
||||||
|
blockDiv.remove();
|
||||||
|
});
|
||||||
|
blockDiv.appendChild(deleteBtn);
|
||||||
|
|
||||||
|
if (type === "text") {
|
||||||
|
const textarea = document.createElement("textarea");
|
||||||
|
textarea.placeholder = "Schreibe deinen Textblock...";
|
||||||
|
textarea.value = value;
|
||||||
|
blockDiv.appendChild(textarea);
|
||||||
|
} else if (type === "image") {
|
||||||
|
const fileInput = document.createElement("input");
|
||||||
|
fileInput.type = "file";
|
||||||
|
fileInput.accept = "image/*";
|
||||||
|
|
||||||
|
const imgPreview = document.createElement("img");
|
||||||
|
imgPreview.style.maxWidth = "200px";
|
||||||
|
imgPreview.style.display = "block";
|
||||||
|
imgPreview.style.marginTop = "10px";
|
||||||
|
|
||||||
|
if (value && typeof value === 'string') {
|
||||||
|
if (value.startsWith('uploads/') || value.startsWith('data:image/')) {
|
||||||
|
imgPreview.src = value;
|
||||||
|
blockDiv.setAttribute("data-value", value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileInput.addEventListener("change", function() {
|
||||||
|
if (this.files && this.files[0]) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = function(e) {
|
||||||
|
imgPreview.src = e.target.result;
|
||||||
|
blockDiv.setAttribute("data-value", e.target.result);
|
||||||
|
}
|
||||||
|
reader.readAsDataURL(this.files[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
blockDiv.appendChild(fileInput);
|
||||||
|
blockDiv.appendChild(imgPreview);
|
||||||
|
}
|
||||||
|
|
||||||
|
container.appendChild(blockDiv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// beim Abschicken verbleibende Blöcke auslesen UND gelöschte Bilder ermitteln
|
||||||
|
form.addEventListener("submit", function(e) {
|
||||||
|
const blocks = [];
|
||||||
|
const currentImages = [];
|
||||||
|
|
||||||
|
// alle aktuell im Formular verbliebenen Blöcke scannen
|
||||||
|
container.querySelectorAll(".editor-block").forEach(blockDiv => {
|
||||||
|
const type = blockDiv.getAttribute("data-type");
|
||||||
|
let value = "";
|
||||||
|
|
||||||
|
if (type === "text") {
|
||||||
|
value = blockDiv.querySelector("textarea").value;
|
||||||
|
} else if (type === "image") {
|
||||||
|
|
||||||
|
const imgTag = blockDiv.querySelector("img");
|
||||||
|
if (imgTag) {
|
||||||
|
const srcValue = imgTag.getAttribute("src") || "";
|
||||||
|
// Wenn es ein neues Bild ist, nutzen wir das data-value (Base64)
|
||||||
|
if (srcValue.startsWith('data:image/')) {
|
||||||
|
value = blockDiv.getAttribute("data-value") || "";
|
||||||
|
} else {
|
||||||
|
value = srcValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pfade sammeln, die der Nutzer NICHT gelöscht hat (für den Abgleich)
|
||||||
|
if (value && value.startsWith('uploads/')) {
|
||||||
|
currentImages.push(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blocks.push({ type: type, value: value });
|
||||||
|
});
|
||||||
|
|
||||||
|
// das reguläre unsichtbare Content-Feld befüllen
|
||||||
|
hiddenContentInput.value = JSON.stringify(blocks);
|
||||||
|
|
||||||
|
// Differenz berechnen: Welche Bilder aus 'initialImages' fehlen in 'currentImages' ?
|
||||||
|
const deletedImages = initialImages.filter(img => !currentImages.includes(img));
|
||||||
|
|
||||||
|
// das 'deleted_images'-Feld dynamisch befüllen und an den Controller senden
|
||||||
|
let deletedInput = document.getElementById("deleted-images");
|
||||||
|
if (!deletedInput) {
|
||||||
|
deletedInput = document.createElement("input");
|
||||||
|
deletedInput.type = "hidden";
|
||||||
|
deletedInput.id = "deleted-images";
|
||||||
|
deletedInput.name = "deleted_images";
|
||||||
|
form.appendChild(deletedInput);
|
||||||
|
}
|
||||||
|
deletedInput.value = JSON.stringify(deletedImages);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Existierende Blöcke laden (stellt alte Daten aus der Session wieder her)
|
||||||
|
try {
|
||||||
|
const initialBlocks = JSON.parse(hiddenContentInput.value.trim());
|
||||||
|
if (Array.isArray(initialBlocks)) {
|
||||||
|
initialBlocks.forEach(b => {
|
||||||
|
if (b.type === "image" && b.value && typeof b.value === 'string' && !b.value.startsWith('data:image/')) {
|
||||||
|
let cleanPath = b.value.trim().replace(/\\\//g, '/'); // Verwandelt \/ in /
|
||||||
|
|
||||||
|
initialImages.push(cleanPath);
|
||||||
|
addBlockElement(b.type, cleanPath);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
addBlockElement(b.type, b.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log("Erfolgreich registrierte Start-Bilder:", initialImages);
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
if (hiddenContentInput.value.trim() !== "") {
|
||||||
|
addBlockElement("text", hiddenContentInput.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SICHERER START: Prüft, ob das HTML bereits bereit ist
|
||||||
|
if (document.readyState === "loading") {
|
||||||
|
document.addEventListener("DOMContentLoaded", initEditor);
|
||||||
|
} else {
|
||||||
|
// Falls das DOM schon fertig geladen ist, führen wir es direkt aus
|
||||||
|
initEditor();
|
||||||
|
}
|
||||||
+101
@@ -0,0 +1,101 @@
|
|||||||
|
let currentClientPage = 1;
|
||||||
|
const itemsPerPage = 10;
|
||||||
|
|
||||||
|
function initFilter() {
|
||||||
|
const filterSelect = document.getElementById('category-filter');
|
||||||
|
const listContainer = document.querySelector('.s-res-list');
|
||||||
|
|
||||||
|
if (!filterSelect || !listContainer) return;
|
||||||
|
|
||||||
|
updateVisibility();
|
||||||
|
|
||||||
|
filterSelect.addEventListener('change', function() {
|
||||||
|
currentClientPage = 1;
|
||||||
|
updateVisibility();
|
||||||
|
});
|
||||||
|
|
||||||
|
const navigationContainer = document.querySelector('.s-res-page-navigation');
|
||||||
|
if (navigationContainer) {
|
||||||
|
navigationContainer.addEventListener('click', function(e) {
|
||||||
|
const button = e.target.closest('.s-res-page-btn');
|
||||||
|
if (!button || button.disabled) return;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const targetPage = button.getAttribute('data-page');
|
||||||
|
if (targetPage) {
|
||||||
|
currentClientPage = parseInt(targetPage, 10);
|
||||||
|
updateVisibility();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateVisibility() {
|
||||||
|
const filterSelect = document.getElementById('category-filter');
|
||||||
|
const listContainer = document.querySelector('.s-res-list');
|
||||||
|
const selectedCategory = filterSelect.value.toLowerCase().trim();
|
||||||
|
const cards = listContainer.querySelectorAll('.s-res-item');
|
||||||
|
|
||||||
|
let visibleCards = [];
|
||||||
|
cards.forEach(card => {
|
||||||
|
const cardCategory = (card.getAttribute('data-category') || '').toLowerCase().trim();
|
||||||
|
if (selectedCategory === 'all' || cardCategory.includes(selectedCategory) || selectedCategory.includes(cardCategory)) {
|
||||||
|
visibleCards.push(card);
|
||||||
|
} else {
|
||||||
|
card.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const totalVisible = visibleCards.length;
|
||||||
|
const totalPages = Math.max(1, Math.ceil(totalVisible / itemsPerPage));
|
||||||
|
|
||||||
|
if (currentClientPage < 1) currentClientPage = 1;
|
||||||
|
if (currentClientPage > totalPages) currentClientPage = totalPages;
|
||||||
|
|
||||||
|
const startOffset = (currentClientPage - 1) * itemsPerPage;
|
||||||
|
const endOffset = startOffset + itemsPerPage;
|
||||||
|
|
||||||
|
visibleCards.forEach((card, index) => {
|
||||||
|
if (index >= startOffset && index < endOffset) {
|
||||||
|
card.style.display = 'flex';
|
||||||
|
} else {
|
||||||
|
card.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updatePaginatorUI(currentClientPage, totalPages);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePaginatorUI(currentPage, totalPages) {
|
||||||
|
const prevBtn = document.getElementById('prev-page-btn');
|
||||||
|
const nextBtn = document.getElementById('next-page-btn');
|
||||||
|
const numbersContainer = document.getElementById('dynamic-page-numbers');
|
||||||
|
|
||||||
|
if (!prevBtn || !nextBtn || !numbersContainer) return;
|
||||||
|
|
||||||
|
prevBtn.setAttribute('data-page', currentPage - 1);
|
||||||
|
prevBtn.disabled = (currentPage <= 1);
|
||||||
|
|
||||||
|
nextBtn.setAttribute('data-page', currentPage + 1);
|
||||||
|
nextBtn.disabled = (currentPage >= totalPages);
|
||||||
|
|
||||||
|
let buttonsHTML = '';
|
||||||
|
for (let i = 1; i <= totalPages; i++) {
|
||||||
|
const activeClass = (i === currentPage) ? 's-res-page-btn-active' : '';
|
||||||
|
buttonsHTML += `<button type="button" class="s-res-page-btn ${activeClass}" data-page="${i}">${i}</button> `;
|
||||||
|
}
|
||||||
|
numbersContainer.innerHTML = buttonsHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hilfsfunktion für Math.ceil in JS
|
||||||
|
function ceil(val) { return Math.ceil(val); }
|
||||||
|
|
||||||
|
// ist das DOM bereits vollständig aufgebaut?
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
// Falls noch geladen wird, auf das Event warten
|
||||||
|
document.addEventListener('DOMContentLoaded', initFilter);
|
||||||
|
} else {
|
||||||
|
// Falls das HTML bereits komplett da ist, sofort ausführen
|
||||||
|
initFilter();
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
function initPaginator() {
|
||||||
|
const form = document.getElementById('search-form-id');
|
||||||
|
const pageInput = document.getElementById('s-res-page-input');
|
||||||
|
const pageButtons = document.querySelectorAll('.s-res-page-navigation .s-res-page-btn');
|
||||||
|
|
||||||
|
pageButtons.forEach(button => {
|
||||||
|
button.addEventListener('click', function() {
|
||||||
|
if (this.disabled) return;
|
||||||
|
|
||||||
|
const targetPage = this.getAttribute('data-page');
|
||||||
|
|
||||||
|
if (targetPage && form && pageInput) {
|
||||||
|
pageInput.value = targetPage;
|
||||||
|
form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ist das DOM bereits vollständig aufgebaut?
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
// Falls noch geladen wird, auf das Event warten
|
||||||
|
document.addEventListener('DOMContentLoaded', initPaginator);
|
||||||
|
} else {
|
||||||
|
// Falls das HTML bereits komplett da ist, sofort ausführen
|
||||||
|
initPaginator();
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
|
||||||
|
function initSorter() {
|
||||||
|
const listContainer = document.querySelector('.s-res-list');
|
||||||
|
const sortRadios = document.querySelectorAll('.sort-radio');
|
||||||
|
|
||||||
|
// wenn keine liste vorhanden, abbrechen
|
||||||
|
if (!listContainer || sortRadios.length === 0) return;
|
||||||
|
|
||||||
|
sortRadios.forEach(radio => {
|
||||||
|
radio.addEventListener('change', function() {
|
||||||
|
const cards = Array.from(listContainer.querySelectorAll('.s-res-item'));
|
||||||
|
const sortValue = this.value;
|
||||||
|
|
||||||
|
cards.sort((a, b) => {
|
||||||
|
if (sortValue === 'likes') {
|
||||||
|
const likesA = parseInt(a.getAttribute('data-likes') || '0', 10);
|
||||||
|
const likesB = parseInt(b.getAttribute('data-likes') || '0', 10);
|
||||||
|
return likesB - likesA;
|
||||||
|
}
|
||||||
|
else if (sortValue === 'alphabet') {
|
||||||
|
// alphabetische sortierung
|
||||||
|
const titleA = a.querySelector('.s-res-link').textContent.trim().toLowerCase();
|
||||||
|
const titleB = b.querySelector('.s-res-link').textContent.trim().toLowerCase();
|
||||||
|
return titleA.localeCompare(titleB);
|
||||||
|
}
|
||||||
|
else if (sortValue === 'newest' || sortValue === 'oldest') {
|
||||||
|
// hoehere ID wird als neuer gesehen
|
||||||
|
const urlA = a.querySelector('.s-res-link').getAttribute('href');
|
||||||
|
const urlB = b.querySelector('.s-res-link').getAttribute('href');
|
||||||
|
|
||||||
|
const idA = parseInt(urlA.match(/id=(\d+)/)[1], 10);
|
||||||
|
const idB = parseInt(urlB.match(/id=(\d+)/)[1], 10);
|
||||||
|
|
||||||
|
return sortValue === 'newest' ? idB - idA : idA - idB;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
listContainer.innerHTML = '';
|
||||||
|
cards.forEach(card => listContainer.appendChild(card));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ist das DOM bereits vollständig aufgebaut?
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', initSorter);
|
||||||
|
} else {
|
||||||
|
initSorter();
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Content-Type: application/json");
|
||||||
|
|
||||||
|
require_once "../model/CommentManager.php";
|
||||||
|
|
||||||
|
if (!isset($_SESSION["user_email"])) {
|
||||||
|
echo json_encode([
|
||||||
|
"success" => false,
|
||||||
|
"message" => "Du musst angemeldet sein, um zu kommentieren."
|
||||||
|
]);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$articleId = $_POST["article_id"] ?? null;
|
||||||
|
$content = trim($_POST["content"] ?? "");
|
||||||
|
$parentCommentId = $_POST["parent_comment_id"] ?? null;
|
||||||
|
|
||||||
|
if ($parentCommentId === "" || $parentCommentId === "0") {
|
||||||
|
$parentCommentId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($articleId) || empty($content)) {
|
||||||
|
echo json_encode([
|
||||||
|
"success" => false,
|
||||||
|
"message" => "Kommentar darf nicht leer sein."
|
||||||
|
]);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$commentManager = CommentManager::getInstance();
|
||||||
|
|
||||||
|
$commentId = $commentManager->addComment(
|
||||||
|
$articleId,
|
||||||
|
$_SESSION["user_email"],
|
||||||
|
$content,
|
||||||
|
$parentCommentId
|
||||||
|
);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
"success" => true,
|
||||||
|
"commentId" => $commentId,
|
||||||
|
"author" => $_SESSION["user_email"],
|
||||||
|
"content" => $content,
|
||||||
|
"created" => date("Y-m-d H:i:s"),
|
||||||
|
"parentCommentId" => $parentCommentId
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo json_encode([
|
||||||
|
"success" => false,
|
||||||
|
"message" => "Kommentar konnte nicht gespeichert werden."
|
||||||
|
]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
<?php
|
||||||
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
require_once '../model/LocalArticleManager.php';
|
||||||
|
require_once '../model/ArticleManager.php';
|
||||||
|
require_once '../validator/article-validator.php';
|
||||||
|
|
||||||
|
if (!isset($_SESSION["user"])) {
|
||||||
|
header("Location: index.php?pfad=login");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
|
$_SESSION["old_title"] = $_POST["title"] ?? '';
|
||||||
|
$_SESSION["old_content"] = $_POST["content"] ?? '';
|
||||||
|
$_SESSION["old_category"] = $_POST["category"] ?? '';
|
||||||
|
$_SESSION["old_tags"] = $_POST["tags"] ?? '';
|
||||||
|
|
||||||
|
if(!isset($_POST["title"]) ||!isset($_POST["content"]) || !isset($_POST["category"])){
|
||||||
|
$_SESSION["message"] = "missing_parameters";
|
||||||
|
header("location: ../../index.php?pfad=createArticle");
|
||||||
|
exit();
|
||||||
|
} else {
|
||||||
|
$title = $_POST["title"];
|
||||||
|
$content = $_POST["content"];
|
||||||
|
$author = $_SESSION["user_email"];
|
||||||
|
$category = $_POST["category"];
|
||||||
|
$tags = $_POST['tags'] ?? '';
|
||||||
|
|
||||||
|
// -------------------------------- Validierung der Daten: -------------------------
|
||||||
|
if (!articleTitleValidator($title)) {
|
||||||
|
$_SESSION["message"] = "invalid_title";
|
||||||
|
header("location: ../../index.php?pfad=createArticle");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!articleContentValidator($content)) {
|
||||||
|
$_SESSION["message"] = "invalid_content";
|
||||||
|
header("location: ../../index.php?pfad=createArticle");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!articleCategoryValidator($category)) {
|
||||||
|
$_SESSION["message"] = "invalid_category";
|
||||||
|
header("location: ../../index.php?pfad=createArticle");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!articleTagValidator($tags)) {
|
||||||
|
$_SESSION["message"] = "invalid_tags";
|
||||||
|
header("location: ../../index.php?pfad=createArticle");
|
||||||
|
exit();
|
||||||
|
} else {
|
||||||
|
$cleanedTags = [];
|
||||||
|
$rawTags = explode(',', $tags);
|
||||||
|
foreach ($rawTags as $rawTag) {
|
||||||
|
// Leerzeichen am Anfang/Ende des einzelnen Tags entfernen:
|
||||||
|
$tag = trim($rawTag);
|
||||||
|
$cleanedTags[] = $tag;
|
||||||
|
}
|
||||||
|
// Duplikate entfernen:
|
||||||
|
$cleanedTags = array_unique($cleanedTags);
|
||||||
|
$cleanedTags = implode(',', $cleanedTags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------- Base64-Bilder verarbeiten und auf Server speichern -----------------
|
||||||
|
$blocks = json_decode($content, true);
|
||||||
|
$uploadDir = __DIR__ . '/../../uploads/';
|
||||||
|
|
||||||
|
if (!file_exists($uploadDir)) {
|
||||||
|
mkdir($uploadDir, 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($blocks)) {
|
||||||
|
foreach ($blocks as &$block) {
|
||||||
|
// sicherstellen, dass 'type' und 'value' existieren:
|
||||||
|
if (isset($block['type']) && isset($block['value']) && $block['type'] === 'image' && str_starts_with($block['value'], 'data:image/')) {
|
||||||
|
|
||||||
|
// Base64-String zerlegen
|
||||||
|
$parts = explode(',', $block['value']);
|
||||||
|
|
||||||
|
// falls der String korrupt ist und kein Komma hat
|
||||||
|
if (count($parts) < 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$metadata = $parts[0];
|
||||||
|
$base64Data = $parts[1];
|
||||||
|
|
||||||
|
// Dateiendung ermitteln
|
||||||
|
preg_match('/data:image\/(?<extension>.*?);/', $metadata, $matches);
|
||||||
|
$extension = $matches['extension'] ?? 'jpg';
|
||||||
|
if ($extension === 'jpeg') {
|
||||||
|
$extension = 'jpg';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eindeutigen Dateinamen generieren
|
||||||
|
$fileName = 'img_' . uniqid() . '.' . $extension;
|
||||||
|
$filePath = $uploadDir . $fileName;
|
||||||
|
|
||||||
|
// Datei im /uploads speichern:
|
||||||
|
if (file_put_contents($filePath, base64_decode($base64Data)) !== false) {
|
||||||
|
// temporären Base64-String durch den echten Pfad ersetzen
|
||||||
|
$block['value'] = 'uploads/' . $fileName;
|
||||||
|
} else {
|
||||||
|
$_SESSION["message"] = "image_upload_error";
|
||||||
|
header("location: ../../index.php?pfad=createArticle");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($block);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aktualisiertes Array wieder in JSON konvertieren
|
||||||
|
$finalContent = json_encode($blocks, JSON_UNESCAPED_UNICODE);
|
||||||
|
|
||||||
|
// ----------------- Übertragung der validierten Daten in ArticleManager: ---------------------------
|
||||||
|
try {
|
||||||
|
$articleManager = ArticleManager::getInstance();
|
||||||
|
$articleManager->addArticle($title, $content, $author, $category, $cleanedTags);
|
||||||
|
|
||||||
|
// Formulardaten nach erfolgreichem Erstellen aus der Session löschen
|
||||||
|
unset($_SESSION["old_title"], $_SESSION["old_content"], $_SESSION["old_category"], $_SESSION["old_tags"]);
|
||||||
|
|
||||||
|
} catch (\Throwable $e){
|
||||||
|
$_SESSION["message"] = "internal_error";
|
||||||
|
header("location: ../../index.php?pfad=createArticle");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION["message"] = "new_article";
|
||||||
|
// Weiterleitung zur Homepage
|
||||||
|
header("location: ../../index.php");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . "/../model/UserManager.php";
|
||||||
|
require_once __DIR__ . "/../model/ArticleManager.php";
|
||||||
|
|
||||||
|
if (!isset($_SESSION["user"])) {
|
||||||
|
header("Location: index.php?pfad=login");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Deregistrierung
|
||||||
|
Funktion: Entfernt User aus der Datenbank und beendet die Session
|
||||||
|
*/
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
$dao = UserManager::getInstance();
|
||||||
|
$articleManager = ArticleManager::getInstance();
|
||||||
|
|
||||||
|
if (isset($_SESSION["user_email"])) {
|
||||||
|
$dao->deleteUser($_SESSION["user_email"]);
|
||||||
|
|
||||||
|
$articles = $articleManager->getArticlesByAuthor($_SESSION["user_email"]);
|
||||||
|
foreach ($articles as $article) {
|
||||||
|
$articleManager->deleteArticle($article->getId(), $_SESSION["user_email"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new NotFoundException("missing_user_email");
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION = [];
|
||||||
|
session_destroy();
|
||||||
|
|
||||||
|
header("Location: ../../index.php");
|
||||||
|
exit();
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
|
||||||
|
$_SESSION["message"] = "internal_error";
|
||||||
|
|
||||||
|
header("Location: ../../index.php?pfad=profile");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . "/../model/ArticleManager.php";
|
||||||
|
|
||||||
|
if (!isset($_SESSION["user"])) {
|
||||||
|
header("Location: index.php?pfad=login");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
|
|
||||||
|
if (isset($_SESSION["user_email"])) {
|
||||||
|
$user = $_SESSION["user_email"];
|
||||||
|
} else {
|
||||||
|
$_SESSION = [];
|
||||||
|
session_destroy();
|
||||||
|
|
||||||
|
header("Location: ../../index.php");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST["id"]) && !empty($_POST["id"])) {
|
||||||
|
$id = $_POST["id"];
|
||||||
|
} else {
|
||||||
|
$_SESSION["message"] = "missing_id";
|
||||||
|
header("location: ../../index.php?pfad=profile");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
$articleManager = ArticleManager::getInstance();
|
||||||
|
$articleManager->deleteArticle($id, $user);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$_SESSION["message"] = $e->getMessage();
|
||||||
|
header("location: ../../index.php?pfad=profile");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION["message"] = "article_deleted";
|
||||||
|
header("location: ../../index.php?pfad=profile");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
require_once 'php/model/Article.php';
|
||||||
|
require_once 'php/model/ArticleManager.php';
|
||||||
|
require_once 'php/model/LocalArticleManager.php';
|
||||||
|
|
||||||
|
$categoriesWithArticles = [];
|
||||||
|
|
||||||
|
$allowedCategories = [
|
||||||
|
'deutsch', 'englisch', 'franzoesisch', 'latein', 'literatur',
|
||||||
|
'mathe', 'biologie', 'chemie', 'physik', 'informatik', 'astronomie',
|
||||||
|
'geschichte', 'erdkunde', 'sozialkunde', 'wirtschaft', 'religion',
|
||||||
|
'ethik', 'philosophie', 'psychologie', 'kunst', 'musik', 'theater',
|
||||||
|
'technik', 'werken', 'hauswirtschaft', 'sport'
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$articleManager = ArticleManager::getInstance();
|
||||||
|
|
||||||
|
foreach ($allowedCategories as $categorySlug) {
|
||||||
|
$allCategoryArticles = $articleManager->getArticlesByCategory($categorySlug);
|
||||||
|
|
||||||
|
// nur Kategorien, die Beiträge enthält:
|
||||||
|
if (!empty($allCategoryArticles) && is_array($allCategoryArticles)) {
|
||||||
|
|
||||||
|
// Beiträge nach Erstellungsdatum sortieren:
|
||||||
|
usort($allCategoryArticles, function($a, $b) {
|
||||||
|
$dateA = strtotime($a->getCreationDate());
|
||||||
|
$dateB = strtotime($b->getCreationDate());
|
||||||
|
return $dateB <=> $dateA; // Absteigende Sortierung
|
||||||
|
});
|
||||||
|
|
||||||
|
// auf die 5 zuletzt hinzugefügten Beiträge begrenzen:
|
||||||
|
$limitedArticles = array_slice($allCategoryArticles, 0, 5);
|
||||||
|
|
||||||
|
$categoriesWithArticles[] = [
|
||||||
|
'slug' => $categorySlug,
|
||||||
|
'name' => ucfirst($categorySlug), // Erster Buchstabe groß für die Ansicht
|
||||||
|
'articles' => $limitedArticles
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception $e){
|
||||||
|
$_SESSION["message"] = "internal_error";
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
?>
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../model/Article.php';
|
||||||
|
require_once __DIR__ . '/../model/ArticleManager.php';
|
||||||
|
|
||||||
|
// 2. Prüfen, ob eine gültige Artikel-ID übergeben wurde
|
||||||
|
if (isset($_GET["id"]) && !empty($_GET["id"])) {
|
||||||
|
$articleId = intval($_GET["id"]);
|
||||||
|
$userEmail = $_SESSION["user_email"];
|
||||||
|
|
||||||
|
if (!isset($_SESSION["user_email"]) || empty($_SESSION["user_email"])) {
|
||||||
|
$_SESSION["message"] = "unauthorized_access";
|
||||||
|
header("Location: ../../index.php?pfad=showArticle&id=" . $articleId);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$articleManager = ArticleManager::getInstance();
|
||||||
|
$articleManager->toggleLike($articleId, $userEmail);
|
||||||
|
|
||||||
|
header("Location: ../../index.php?pfad=showArticle&id=" . $articleId);
|
||||||
|
exit();
|
||||||
|
|
||||||
|
} catch (NotFoundException $e) {
|
||||||
|
$_SESSION["message"] = "missing_id";
|
||||||
|
header("Location: ../../index.php");
|
||||||
|
exit();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$_SESSION["message"] = "internal_error";
|
||||||
|
header("Location: ../../index.php");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$_SESSION["message"] = "missing_id";
|
||||||
|
header("Location: ../../index.php");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once "php/model/UserManager.php";
|
||||||
|
|
||||||
|
$error = null;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Verarbeitung des Login-Formulars
|
||||||
|
Funktion: Prüft Benutzerdaten und erstellt Session für eingeloggten Nutzer
|
||||||
|
*/
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
$dao = UserManager::getInstance();
|
||||||
|
|
||||||
|
$email = $_POST["email"] ?? "";
|
||||||
|
$password = $_POST["password"] ?? "";
|
||||||
|
|
||||||
|
$user = $dao->findUser($email);
|
||||||
|
|
||||||
|
if ($user && password_verify($password, $user["password"])) {
|
||||||
|
|
||||||
|
$_SESSION["user"] = $user["vorname"] . " " . $user["nachname"];
|
||||||
|
$_SESSION["user_email"] = $user["email"];
|
||||||
|
|
||||||
|
header("Location: index.php");
|
||||||
|
exit();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$error = "Login fehlgeschlagen. Bitte überprüfe deine Eingaben.";
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$error = "Es ist ein interner Fehler aufgetreten. Bitte versuche es erneut.";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
$_SESSION = [];
|
||||||
|
session_destroy();
|
||||||
|
|
||||||
|
header("Location: index.php");
|
||||||
|
exit();
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once "php/model/UserManager.php";
|
||||||
|
require_once "php/model/Article.php";
|
||||||
|
require_once "php/model/ArticleManager.php";
|
||||||
|
require_once "php/model/CommentManager.php";
|
||||||
|
require_once "php/validator/user-validator.php";
|
||||||
|
|
||||||
|
$error = null;
|
||||||
|
|
||||||
|
if (!isset($_SESSION["user"])) {
|
||||||
|
header("Location: index.php?pfad=login");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$dao = UserManager::getInstance();
|
||||||
|
|
||||||
|
$user = $dao->findUser($_SESSION["user_email"] ?? "");
|
||||||
|
|
||||||
|
if (!$user) {
|
||||||
|
$_SESSION = [];
|
||||||
|
session_destroy();
|
||||||
|
|
||||||
|
header("Location: index.php?pfad=login");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["saveProfile"])) {
|
||||||
|
$oldEmail = $_SESSION["user_email"];
|
||||||
|
|
||||||
|
$newEmail = trim($_POST["email"] ?? "");
|
||||||
|
$vorname = trim($_POST["vorname"] ?? "");
|
||||||
|
$nachname = trim($_POST["nachname"] ?? "");
|
||||||
|
$password = $_POST["password"] ?? "";
|
||||||
|
|
||||||
|
if (!userEmailValidator($newEmail)) {
|
||||||
|
$error = "Bitte gib eine gültige E-Mail-Adresse ein.";
|
||||||
|
$_GET["edit"] = "1";
|
||||||
|
|
||||||
|
} elseif (!userNameValidator($vorname)) {
|
||||||
|
$error = "Der Vorname muss 2 bis 20 Zeichen lang sein und darf nur Buchstaben, Leerzeichen und Bindestriche enthalten.";
|
||||||
|
$_GET["edit"] = "1";
|
||||||
|
|
||||||
|
} elseif (!userNameValidator($nachname)) {
|
||||||
|
$error = "Der Nachname muss 2 bis 20 Zeichen lang sein und darf nur Buchstaben, Leerzeichen und Bindestriche enthalten.";
|
||||||
|
$_GET["edit"] = "1";
|
||||||
|
|
||||||
|
} elseif (!userOptionalPasswordValidator($password)) {
|
||||||
|
$error = "Das Passwort muss 5 bis 12 Zeichen lang sein.";
|
||||||
|
$_GET["edit"] = "1";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$updated = $dao->updateUser(
|
||||||
|
$oldEmail,
|
||||||
|
$newEmail,
|
||||||
|
$vorname,
|
||||||
|
$nachname,
|
||||||
|
$password
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($updated) {
|
||||||
|
$_SESSION["user"] = $vorname . " " . $nachname;
|
||||||
|
$_SESSION["user_email"] = $newEmail;
|
||||||
|
|
||||||
|
$_SESSION["message"] = "profile_updated";
|
||||||
|
header("Location: index.php?pfad=profile");
|
||||||
|
exit();
|
||||||
|
} else {
|
||||||
|
$error = "Die Daten konnten nicht gespeichert werden.";
|
||||||
|
$_GET["edit"] = "1";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = $dao->findUser($_SESSION["user_email"] ?? "");
|
||||||
|
|
||||||
|
$articleManager = ArticleManager::getInstance();
|
||||||
|
$userArticles = $articleManager->getArticlesByAuthor($_SESSION["user_email"]);
|
||||||
|
|
||||||
|
$commentManager = CommentManager::getInstance();
|
||||||
|
$userComments = $commentManager->getCommentsByAuthor($_SESSION["user_email"]);
|
||||||
|
|
||||||
|
if (!isset($userArticles)) {
|
||||||
|
$_SESSION["message"] = "user_has_no_articles";
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (InvalidArgumentException $e) {
|
||||||
|
$error = $e->getMessage();
|
||||||
|
$_GET["edit"] = "1";
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$error = "Es ist ein interner Fehler aufgetreten. Bitte versuche es erneut.";
|
||||||
|
$_SESSION["message"] = "internal_error";
|
||||||
|
$_GET["edit"] = "1";
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* Controller für die Liste der eigenen Beiträge eines Nutzers auf der eigenen Profilseite
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once 'php/model/Article.php';
|
||||||
|
require_once 'php/model/ArticleManager.php';
|
||||||
|
require_once "php/model/UserManager.php";
|
||||||
|
|
||||||
|
try {
|
||||||
|
$dao = UserManager::getInstance();
|
||||||
|
$user = $dao->findUser($_SESSION["user_email"] ?? "");
|
||||||
|
|
||||||
|
$author = $user["email"];
|
||||||
|
$articleManager = ArticleManager::getInstance();
|
||||||
|
$userArticles = $articleManager->getArticlesByAuthor($author);
|
||||||
|
if(!isset($userArticles)) {
|
||||||
|
$_SESSION["message"] = "user_has_no_articles";
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$_SESSION["message"] = "internal_error";
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once "php/model/UserManager.php";
|
||||||
|
require_once "php/validator/user-validator.php";
|
||||||
|
|
||||||
|
$error = null;
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
|
|
||||||
|
$email = trim($_POST["email"] ?? "");
|
||||||
|
$vorname = trim($_POST["vorname"] ?? "");
|
||||||
|
$nachname = trim($_POST["nachname"] ?? "");
|
||||||
|
$plainPassword = $_POST["password"] ?? "";
|
||||||
|
|
||||||
|
if (!userEmailValidator($email)) {
|
||||||
|
$error = "Bitte gib eine gültige E-Mail-Adresse ein.";
|
||||||
|
} elseif (!userNameValidator($vorname)) {
|
||||||
|
$error = "Der Vorname muss 2 bis 20 Zeichen lang sein und darf nur Buchstaben, Umlaute, Leerzeichen und Bindestriche enthalten.";
|
||||||
|
} elseif (!userNameValidator($nachname)) {
|
||||||
|
$error = "Der Nachname muss 2 bis 20 Zeichen lang sein und darf nur Buchstaben, Umlaute, Leerzeichen und Bindestriche enthalten.";
|
||||||
|
} elseif (!userPasswordValidator($plainPassword)) {
|
||||||
|
$error = "Das Passwort muss 5 bis 12 Zeichen lang sein.";
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
$dao = UserManager::getInstance();
|
||||||
|
|
||||||
|
$password = password_hash($plainPassword, PASSWORD_DEFAULT);
|
||||||
|
|
||||||
|
$dao->addUser($email, $vorname, $nachname, $password);
|
||||||
|
|
||||||
|
$_SESSION["user"] = $vorname . " " . $nachname;
|
||||||
|
$_SESSION["user_email"] = $email;
|
||||||
|
|
||||||
|
header("Location: index.php");
|
||||||
|
exit();
|
||||||
|
|
||||||
|
} catch (InvalidArgumentException $e) {
|
||||||
|
$error = $e->getMessage();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$error = "Die Registrierung konnte nicht gespeichert werden.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
require_once '../model/LocalArticleManager.php';
|
||||||
|
require_once '../model/ArticleManager.php';
|
||||||
|
require_once '../model/Article.php';
|
||||||
|
require_once '../validator/search-validator.php';
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
|
||||||
|
|
||||||
|
$search = trim($_GET["q"]);
|
||||||
|
if (!searchQueryValidator($search)) {
|
||||||
|
$_SESSION["search_results"] = [];
|
||||||
|
$_SESSION["search_query"] = "";
|
||||||
|
$_SESSION["message"] = "invalid_search_query";
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
|
||||||
|
$articleManager = ArticleManager::getInstance();
|
||||||
|
|
||||||
|
$results = $articleManager->search($search);
|
||||||
|
|
||||||
|
$sortStyle = $_GET['sort'] ?? 'alphabet';
|
||||||
|
$_SESSION['search_sort'] = $sortStyle;
|
||||||
|
|
||||||
|
if ($sortStyle === 'alphabet') {
|
||||||
|
// Titel aufsteigend alphabetiisch sortiert
|
||||||
|
usort($results, function ($a, $b) {
|
||||||
|
return strcasecmp($a->getTitle(), $b->getTitle());
|
||||||
|
});
|
||||||
|
} elseif ($sortStyle === 'likes') {
|
||||||
|
usort($results, function($a, $b) {
|
||||||
|
return $b->getLikeCount() <=> $a->getLikeCount();
|
||||||
|
});
|
||||||
|
} elseif ($sortStyle === 'newest') {
|
||||||
|
// Datum neu zu alt sortiert
|
||||||
|
usort($results, function($a, $b) {
|
||||||
|
return strcmp($b->getCreationDate(), $a->getCreationDate());
|
||||||
|
});
|
||||||
|
} elseif ($sortStyle === 'oldest') {
|
||||||
|
// Datum alt zu neu sortiert
|
||||||
|
usort($results, function($a, $b) {
|
||||||
|
return strcmp($a->getCreationDate(), $b->getCreationDate());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ergebnisse werden in ein flaches array umgewandelt, da sont incomplete-PHP error im Ergebnis
|
||||||
|
$safeArrayResults = [];
|
||||||
|
foreach ($results as $obj) {
|
||||||
|
$safeArrayResults[] = [
|
||||||
|
"id" => $obj->getId(),
|
||||||
|
"title" => $obj->getTitle(),
|
||||||
|
"content" => $obj->getContent(),
|
||||||
|
"author" => $obj->getAuthor(),
|
||||||
|
"category" => $obj->getCategory(),
|
||||||
|
"tags" => $obj->getTags(),
|
||||||
|
"creationDate" => $obj->getCreationDate(),
|
||||||
|
"likes" => $obj->getLikes(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION["search_results"] = $safeArrayResults;
|
||||||
|
$_SESSION["search_query"] = $search;
|
||||||
|
$_SESSION["message"] = "new_search_results";
|
||||||
|
|
||||||
|
} catch (Exception $e){
|
||||||
|
$_SESSION["message"] = "internal_error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$sort = $_GET['sort'] ?? 'alphabet';
|
||||||
|
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 10;
|
||||||
|
if (!searchLimitValidator($limit)) {
|
||||||
|
$limit = 10;
|
||||||
|
}
|
||||||
|
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||||
|
header("Location: ../../index.php?pfad=search-results&q=" . urlencode($search) . "&sort=" . urlencode($sort) . "&limit=" . $limit . "&page=" . $page);
|
||||||
|
exit();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once 'php/model/Article.php';
|
||||||
|
require_once 'php/model/ArticleManager.php';
|
||||||
|
require_once 'php/model/CommentManager.php';
|
||||||
|
|
||||||
|
if (isset($_GET["id"]) && !empty($_GET["id"])){
|
||||||
|
try {
|
||||||
|
$id = $_GET["id"];
|
||||||
|
$articleManager = ArticleManager::getInstance();
|
||||||
|
$article = $articleManager->getArticle($id);
|
||||||
|
if($article != null){
|
||||||
|
$title = $article->getTitle();
|
||||||
|
$content = $article->getContent();
|
||||||
|
$category = $article->getCategory();
|
||||||
|
$author = $article->getAuthor();
|
||||||
|
$tags = $article->getTags();
|
||||||
|
$articleObj = $article; // Objekt für die Like-Abfagen sichern
|
||||||
|
}else{
|
||||||
|
//header("location: index.php?pfad=404");
|
||||||
|
include_once "content/404.php";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$commentManager = CommentManager::getInstance();
|
||||||
|
$comments = $commentManager->getCommentsByArticle($_GET["id"]);
|
||||||
|
|
||||||
|
foreach ($comments as $comment) {
|
||||||
|
if ($comment->isReply()) {
|
||||||
|
$parentId = $comment->getParentCommentId();
|
||||||
|
$repliesByParent[$parentId][] = $comment;
|
||||||
|
} else {
|
||||||
|
$mainComments[] = $comment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception $e){
|
||||||
|
$_SESSION["message"] = "internal_error";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
$_SESSION["message"] = "missing_id";
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'php/model/Article.php';
|
||||||
|
require_once 'php/model/ArticleManager.php';
|
||||||
|
require_once 'php/validator/article-validator.php';
|
||||||
|
|
||||||
|
if (isset($_GET["category"]) && !empty($_GET["category"]) && articleCategoryValidator($_GET["category"])){
|
||||||
|
$category = $_GET["category"];
|
||||||
|
try {
|
||||||
|
$articleManager = ArticleManager::getInstance();
|
||||||
|
$articles = $articleManager->getArticlesByCategory($category);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$_SESSION["message"] = "internal_error";
|
||||||
|
include_once "content/404.php";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
$_SESSION["message"] = "invalid_category";
|
||||||
|
include_once "content/404.php";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
<?php
|
||||||
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once '../model/LocalArticleManager.php';
|
||||||
|
require_once '../model/ArticleManager.php';
|
||||||
|
require_once '../model/Article.php';
|
||||||
|
require_once '../validator/article-validator.php';
|
||||||
|
|
||||||
|
if (!isset($_SESSION["user"])) {
|
||||||
|
header("Location: index.php?pfad=login");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
|
$_SESSION["old_title"] = $_POST["title"] ?? '';
|
||||||
|
$_SESSION["old_content"] = $_POST["content"] ?? '';
|
||||||
|
$_SESSION["old_category"] = $_POST["category"] ?? ''; // TODO: die Kategorie im Dropdown setzen, wenn der Editor erneut geöffnet wird.
|
||||||
|
$_SESSION["old_tags"] = $_POST["tags"] ?? '';
|
||||||
|
|
||||||
|
if (isset($_GET["id"]) && !empty($_GET["id"])) {
|
||||||
|
$id = $_GET["id"];
|
||||||
|
} else {
|
||||||
|
$_SESSION["message"] = "missing_id";
|
||||||
|
header("location: ../../index.php?pfad=updateArticle");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$articleManager = ArticleManager::getInstance();
|
||||||
|
$article = $articleManager->getArticle($id);
|
||||||
|
if ($article->getAuthor() != $_SESSION["user"]->getUsername()) {
|
||||||
|
$_SESSION["message"] = "unauthorized_access";
|
||||||
|
header("location: ../../index.php");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$_SESSION["message"] = $e->getMessage();
|
||||||
|
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($_POST["title"]) ||!isset($_POST["content"]) || !isset($_POST["category"])){
|
||||||
|
$_SESSION["message"] = "missing_parameters";
|
||||||
|
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
||||||
|
exit();
|
||||||
|
}else{
|
||||||
|
$title = $_POST["title"];
|
||||||
|
$content = $_POST["content"];
|
||||||
|
$author = $_SESSION["user_email"];
|
||||||
|
$category = $_POST["category"];
|
||||||
|
$tags = $_POST['tags'] ?? '';
|
||||||
|
|
||||||
|
// -------------------------------- Validierung der Daten: -------------------------
|
||||||
|
if (!articleTitleValidator($title)) {
|
||||||
|
$_SESSION["message"] = "invalid_title";
|
||||||
|
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!articleContentValidator($content)) {
|
||||||
|
$_SESSION["message"] = "invalid_content";
|
||||||
|
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!articleCategoryValidator($category)) {
|
||||||
|
$_SESSION["message"] = "invalid_category";
|
||||||
|
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!articleTagValidator($tags)) {
|
||||||
|
$_SESSION["message"] = "invalid_tags";
|
||||||
|
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
||||||
|
exit();
|
||||||
|
} else {
|
||||||
|
$cleanedTags = [];
|
||||||
|
$rawTags = explode(',', $tags);
|
||||||
|
foreach ($rawTags as $rawTag) {
|
||||||
|
// Leerzeichen am Anfang/Ende des einzelnen Tags entfernen:
|
||||||
|
$tag = trim($rawTag);
|
||||||
|
$cleanedTags[] = $tag;
|
||||||
|
}
|
||||||
|
// Duplikate entfernen:
|
||||||
|
$cleanedTags = array_unique($cleanedTags);
|
||||||
|
$cleanedTags = implode(',', $cleanedTags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------- Base64-Bilder speichern ---------------------------------------------
|
||||||
|
$blocks = json_decode($content, true);
|
||||||
|
$uploadDir = __DIR__ . '/../../uploads/';
|
||||||
|
|
||||||
|
if (!file_exists($uploadDir)) {
|
||||||
|
mkdir($uploadDir, 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------- Gelöschte Bilder über die JS-Löschliste entfernen ----------------- TODO: Gelöschte Bilder über die JS-Löschliste entfernen
|
||||||
|
/*if (isset($_POST['deleted_images'])) {
|
||||||
|
$deletedImages = json_decode($_POST['deleted_images'], true);
|
||||||
|
|
||||||
|
// Wir ermitteln den physisch echten, absoluten Pfad zum uploads-Ordner auf der Festplatte
|
||||||
|
$uploadDir = realpath(__DIR__ . '/../../uploads') . DIRECTORY_SEPARATOR;
|
||||||
|
|
||||||
|
if (is_array($deletedImages)) {
|
||||||
|
foreach ($deletedImages as $imagePath) {
|
||||||
|
// Nur den reinen Dateinamen heraustrennen (z.B. img_65a123.jpg)
|
||||||
|
$filename = basename($imagePath);
|
||||||
|
$fullDeletePath = $uploadDir . $filename;
|
||||||
|
|
||||||
|
// Debugging & Löschen:
|
||||||
|
if (file_exists($fullDeletePath)) {
|
||||||
|
// Versuchen zu löschen. Wenn es fehlschlägt, Fehlermeldung erzwingen
|
||||||
|
if (!@unlink($fullDeletePath)) {
|
||||||
|
$error = error_get_last();
|
||||||
|
die("Datei existiert, aber PHP darf sie nicht löschen! Grund: " . $error['message']);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Wenn PHP die Datei an diesem Pfad nicht findet, brechen wir zum Debuggen ab
|
||||||
|
// die("PHP findet die Datei nicht unter dem Pfad: " . $fullDeletePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// ----------------------- NEU hinzugefügte Base64-Bilder: --------------------------
|
||||||
|
if (is_array($blocks)) {
|
||||||
|
foreach ($blocks as &$block) {
|
||||||
|
// Prüfen, ob der Block ein Bild ist und ein NEUES Bild (Base64-Format) enthält
|
||||||
|
if (isset($block['type']) && isset($block['value']) && $block['type'] === 'image' && is_string($block['value'])) {
|
||||||
|
|
||||||
|
if (str_starts_with($block['value'], 'data:image/')) {
|
||||||
|
$parts = explode(',', $block['value']);
|
||||||
|
if (count($parts) >= 2) {
|
||||||
|
$metadata = $parts[0];
|
||||||
|
$base64Data = $parts[1];
|
||||||
|
|
||||||
|
preg_match('/data:image\/(?<extension>.*?);/', $metadata, $matches);
|
||||||
|
$extension = $matches['extension'] ?? 'jpg';
|
||||||
|
if ($extension === 'jpeg') { $extension = 'jpg'; }
|
||||||
|
|
||||||
|
$fileName = 'img_' . uniqid() . '.' . $extension;
|
||||||
|
$filePath = $uploadDir . $fileName;
|
||||||
|
|
||||||
|
if (file_put_contents($filePath, base64_decode($base64Data)) !== false) {
|
||||||
|
$block['value'] = 'uploads/' . $fileName;
|
||||||
|
} else {
|
||||||
|
$_SESSION["message"] = "image_upload_error";
|
||||||
|
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($block);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aktualisiertes Array wieder in JSON konvertieren
|
||||||
|
$finalContent = json_encode($blocks, JSON_UNESCAPED_UNICODE);
|
||||||
|
|
||||||
|
// ----------------- Übertragung der validierten Daten in ArticleManager: ---------------------------
|
||||||
|
try {
|
||||||
|
$articleManager = ArticleManager::getInstance();
|
||||||
|
$article = $articleManager->getArticle($id);
|
||||||
|
$article->setTitle($title);
|
||||||
|
$article->setContent($finalContent);
|
||||||
|
$article->setCategory($category);
|
||||||
|
$article->setTags($cleanedTags);
|
||||||
|
$articleManager->updateArticle($id ,$article, $author);
|
||||||
|
|
||||||
|
unset($_SESSION["old_title"], $_SESSION["old_content"], $_SESSION["old_category"], $_SESSION["old_tags"]);
|
||||||
|
|
||||||
|
} catch (\Throwable $e){
|
||||||
|
$_SESSION["message"] = $e->getMessage();
|
||||||
|
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
$_SESSION["message"] = "article_updated";
|
||||||
|
// Weiterleitung zur Homepage
|
||||||
|
header("location: ../../index.php?pfad=showArticle&id=$id");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Klasse: Artikel
|
||||||
|
* Diese Klasse stellt alle Daten eines Beitrags (Beitrag) bereit
|
||||||
|
*
|
||||||
|
* @author Niklas Ortmann
|
||||||
|
*/
|
||||||
|
class Article
|
||||||
|
{
|
||||||
|
private $id;
|
||||||
|
private $title;
|
||||||
|
private $content;
|
||||||
|
private $author;
|
||||||
|
private $creationDate;
|
||||||
|
private $category;
|
||||||
|
private $tags;
|
||||||
|
private $likes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Konstruktor
|
||||||
|
*
|
||||||
|
* @param $id integer ID des Beitrages
|
||||||
|
* @param $title string Titel des Beitrags
|
||||||
|
* @param $content string Inhalt des Beitrags
|
||||||
|
* @param $author string der Autor des des Beitrages NID
|
||||||
|
* @param $category string Kategorie des Beitrages
|
||||||
|
* @param $tags string optionale Schlagworte für eine bessere Suche
|
||||||
|
* @param $creationDate string Datum der Beitragserstellung
|
||||||
|
*/
|
||||||
|
public function __construct(int $id, string $title, string $content, string $author, string $category, string $tags, string $creationDate, array $likes = [])
|
||||||
|
{
|
||||||
|
$this->id = $id;
|
||||||
|
$this->title = $title;
|
||||||
|
$this->content = $content;
|
||||||
|
$this->author = $author;
|
||||||
|
$this->creationDate = $creationDate;
|
||||||
|
$this->category = $category;
|
||||||
|
$this->tags = $tags;
|
||||||
|
$this->likes = $likes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt die ID eines Beitrags zurück.
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getId(): int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt den Titel eines Beitrags zurück.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getTitle(): string
|
||||||
|
{
|
||||||
|
return $this->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt den Titel eines Beitrags
|
||||||
|
* @param $title
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setTitle($title)
|
||||||
|
{
|
||||||
|
$this->title = $title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt den Content eines Beitrags zurück.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getContent(): string
|
||||||
|
{
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt den Content eines Beitrags.
|
||||||
|
*
|
||||||
|
* @param $content
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setContent($content)
|
||||||
|
{
|
||||||
|
$this->content = $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt den Autor eines Beitrags zurück.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getAuthor(): string
|
||||||
|
{
|
||||||
|
return $this->author;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt das Veröffentlichungsdatum des Beitrags zurück.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getCreationDate(): string
|
||||||
|
{
|
||||||
|
return $this->creationDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt die Kategorie eines Beitrags zurück.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getCategory(): string
|
||||||
|
{
|
||||||
|
return $this->category;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt die Kategorie eines Beitrags.
|
||||||
|
* @param string $category
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setCategory(string $category)
|
||||||
|
{
|
||||||
|
$this->category = $category;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt die Schlagworte eines Beitrags zurück.
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getTags(): string
|
||||||
|
{
|
||||||
|
return $this->tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt die Schlagworte eines Beitrags.
|
||||||
|
* @param string $tags
|
||||||
|
*/
|
||||||
|
public function setTags(string $tags)
|
||||||
|
{
|
||||||
|
$this->tags = $tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt alle User-IDs zurück, die diesen Beitrag geliked haben.
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getLikes(): array
|
||||||
|
{
|
||||||
|
return $this->likes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt die Gesamtzahl der Likes zurück.
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getLikeCount(): int
|
||||||
|
{
|
||||||
|
return count($this->likes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft, ob ein bestimmter Nutzer den Beitrag bereits geliked hat.
|
||||||
|
*
|
||||||
|
* @param string $userId
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasLiked(string $userId): bool
|
||||||
|
{
|
||||||
|
return in_array($userId, $this->likes);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'LocalArticleManager.php';
|
||||||
|
require_once 'DatabaseArticleManager.php';
|
||||||
|
require_once 'Article.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Die Klasse beinhaltet alle Methoden für die Operation mit den Beitrags-Daten.
|
||||||
|
*
|
||||||
|
* @author Niklas Ortmann
|
||||||
|
*/
|
||||||
|
class ArticleManager
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Diese Methode erstellt, falls noch keine existiert, eine Instanz einer ArticleManager-Implementierung und
|
||||||
|
* erstellt Dummy-Beiträge.
|
||||||
|
*
|
||||||
|
* @throws InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public static function getInstance()
|
||||||
|
{
|
||||||
|
$articleManager = DatabaseArticleManager::getInstance(); // Hier kann zwischen dem lokalen und datenbankbasiertem ArticleManager gewechselt werden.
|
||||||
|
|
||||||
|
// 100 fiktionale Fachbeiträge:
|
||||||
|
$dummyArticles = [
|
||||||
|
// --- INFORMATIK & MATHE (1-20) ---
|
||||||
|
1 => ["Satz des Pythagoras", "Der Satz des Pythagoras beschreibt das Verhältnis der Seitenlängen in einem rechtwinkligen Dreieck.", "mathe", "Dreiecke, Geometrie"],
|
||||||
|
2 => ["Tunneleffekt", "Der Tunneleffekt beschreibt das quantenmechanische Phänomen, dass Teilchen Barrieren überwinden können.", "physik", "Quantenphysik, Energie"],
|
||||||
|
3 => ["Datenschutz vs Datensicherheit", "Datenschutz schützt Personen, während Datensicherheit Systeme vor unbefugten Zugriffen schützt.", "informatik", "Daten, DSGVO"],
|
||||||
|
4 => ["Einführung in Algorithmen", "Ein Algorithmus ist eine präzise Handlungsanweisung zur Lösung eines vordefinierten Problems.", "informatik", "Code, Logik"],
|
||||||
|
5 => ["Primzahlen im Detail", "Primzahlen sind natürliche Zahlen, die nur durch eins und sich selbst ohne Rest teilbar sind.", "mathe", "Zahlentheorie"],
|
||||||
|
6 => ["Lineare Algebra", "Vektoren und Matrizen bilden das Fundament für moderne Computergrafik und 3D-Engines.", "mathe", "Matrizen, Vektoren"],
|
||||||
|
7 => ["Objektorientierte Programmierung", "Die OOP nutzt Klassen und Objekte, um reale Strukturen im Quellcode abzubilden.", "informatik", "OOP, Klassen"],
|
||||||
|
8 => ["Grundlagen von HTML und CSS", "HTML strukturiert den Inhalt einer Webseite, während CSS für das optische Design zuständig ist.", "informatik", "Web, Frontend"],
|
||||||
|
9 => ["Die Relativitätstheorie", "Einsteins Theorie revolutionierte unser Verständnis von Raum, Zeit und Gravitation massiv.", "physik", "Einstein, Gravitation"],
|
||||||
|
10 => ["Datenbanken und SQL", "Strukturierte Abfragesprachen erlauben das effiziente Speichern und Abrufen großer Datenmengen.", "informatik", "SQL, Datenbank"],
|
||||||
|
11 => ["Wahrscheinlichkeitsrechnung", "Die Stochastik befasst sich mit der mathematischen Analyse von Zufallsexperimenten.", "mathe", "Zufall, Stochastik"],
|
||||||
|
12 => ["Quantencomputing", "Künftige Quantencomputer nutzen Qubits, um komplexe Berechnungen in Rekordzeit zu lösen.", "informatik", "Hardware, Zukunft"],
|
||||||
|
13 => ["Die Fibonacci-Folge", "Diese Zahlenreihe beschreibt mathematische Wachstumsmuster, die oft in der Natur vorkommen.", "mathe", "Zahlen, Natur"],
|
||||||
|
14 => ["Thermodynamik", "Die Hauptsätze der Thermodynamik regeln den Energieaustausch und die Entropie in Systemen.", "physik", "Energie, Wärme"],
|
||||||
|
15 => ["Cybersecurity Grundlagen", "Die Absicherung von Netzwerken erfordert Firewalls, Verschlüsselung und regelmäßige Audits.", "informatik", "Sicherheit, Hacker"],
|
||||||
|
16 => ["Kryptographie", "Asymmetrische Verschlüsselungsverfahren sichern heutzutage den gesamten Datenverkehr im Web.", "informatik", "Krypto, Security"],
|
||||||
|
17 => ["Analysis und Ableitungen", "Die Differentialrechnung untersucht die lokalen Änderungsraten von mathematischen Funktionen.", "mathe", "Analysis, Funktionen"],
|
||||||
|
18 => ["Schwarze Löcher", "Diese Regionen im Raum besitzen eine so starke Gravitation, dass selbst Licht nicht entkommt.", "physik", "Astronomie, Kosmos"],
|
||||||
|
19 => ["Git Versionsverwaltung", "Git erlaubt Entwicklern das parallele Arbeiten an Code-Projekten ohne Datenverlust.", "informatik", "Git, DevOps"],
|
||||||
|
20 => ["Künstliche Intelligenz", "Neuronale Netze versuchen das menschliche Gehirn für maschinelles Lernen nachzubilden.", "informatik", "KI, Software"],
|
||||||
|
|
||||||
|
// --- CHEMIE & BIOLOGIE (21-40) ---
|
||||||
|
21 => ["Das Periodensystem", "Die Elemente sind nach ihrer Ordnungszahl und chemischen Eigenschaften geordnet.", "chemie", "Elemente, Moleküle"],
|
||||||
|
22 => ["Photosynthese", "Pflanzen wandeln mithilfe von Sonnenlicht Kohlendioxid und Wasser in Glukose und Sauerstoff um.", "biologie", "Pflanzen, Energie"],
|
||||||
|
23 => ["Aufbau einer Zelle", "Die Zelle ist die kleinste lebende Einheit aller Organismen mit spezialisierten Organellen.", "biologie", "Zellen, Biologie"],
|
||||||
|
24 => ["Säuren und Basen", "Der pH-Wert misst die Konzentration von Wasserstoff-Ionen in einer wässrigen Lösung.", "chemie", "Labor, pH-Wert"],
|
||||||
|
25 => ["Die DNA-Struktur", "Die Doppelhelix enthält den genetischen Bauplan für die Entwicklung aller Lebewesen.", "biologie", "Genetik, Erbgut"],
|
||||||
|
26 => ["Chemische Bindungen", "Kovalente Bindungen entstehen durch das Teilen von Elektronenpaaren zwischen Atomen.", "chemie", "Atome, Bindung"],
|
||||||
|
27 => ["Das Immunsystem", "Weiße Blutkörperchen und Antikörper schützen den menschlichen Körper vor Krankheitserregern.", "biologie", "Gesundheit, Abwehr"],
|
||||||
|
28 => ["Katalysatoren", "Katalysatoren beschleunigen chemische Reaktionen, ohne dabei selbst verbraucht zu werden.", "chemie", "Reaktion, Chemie"],
|
||||||
|
29 => ["Ökosystem Wald", "Das Zusammenspiel von Flora, Fauna und Klima bildet ein hochsensibles ökologisches System.", "biologie", "Natur, Wald"],
|
||||||
|
30 => ["Die Mendelschen Regeln", "Diese Grundgesetze der Vererbung beschreiben, wie Merkmale an Nachkommen weitergegeben werden.", "biologie", "Genetik, Erbung"],
|
||||||
|
31 => ["Zustandsformen der Materie", "Fest, flüssig und gasförmig sind die klassischen Aggregatzustände von Stoffen.", "chemie", "Physik, Materie"],
|
||||||
|
32 => ["Evolutionstheorie", "Charles Darwin begründete die Theorie der natürlichen Auslese und Anpassung von Arten.", "biologie", "Darwin, Evolution"],
|
||||||
|
33 => ["Organische Chemie", "Die Chemie der Kohlenstoffverbindungen bildet die Basis für alles bekannte Leben.", "chemie", "Kohlenstoff, Chemie"],
|
||||||
|
34 => ["Das menschliche Gehirn", "Milliarden von Neuronen kommunizieren über Synapsen, um Reize und Gedanken zu verarbeiten.", "biologie", "Neurologie, Nerven"],
|
||||||
|
35 => ["Der Wasserkreislauf", "Verdunstung, Kondensation und Niederschlag halten das Wasser auf der Erde in Bewegung.", "geographie", "Wasser, Klima"],
|
||||||
|
36 => ["Aggregatzustände von Wasser", "Wasser zeigt ungewöhnliche Eigenschaften wie die Dichteanomalie beim Gefrieren.", "chemie", "Wasser, Eis"],
|
||||||
|
37 => ["Blutkreislauf des Menschen", "Das Herz pumpt sauerstoffreiches Blut durch Arterien in alle Organe des Körpers.", "biologie", "Herz, Medizin"],
|
||||||
|
38 => ["Das Ohmsche Gesetz", "Es beschreibt den direkten Zusammenhang zwischen Spannung, Stromstärke und Widerstand.", "physik", "Strom, Elektronik"],
|
||||||
|
39 => ["Plattentektonik", "Die Bewegung der Kontinentalplatten führt zu Erdbeben, Vulkanismus und Gebirgsbildung.", "geographie", "Erde, Geologie"],
|
||||||
|
40 => ["Proteine und Enzyme", "Enzyme wirken als Biokatalysatoren und steuern fast alle Stoffwechselprozesse.", "biologie", "Biochemie, Enzyme"],
|
||||||
|
|
||||||
|
// --- GESCHICHTE & WIRTSCHAFT (41-60) ---
|
||||||
|
41 => ["Das Römische Reich", "Vom Stadtstaat zum Weltreich prägte Rom die Rechts- und Kulturgeschichte Europas.", "geschichte", "Antike, Rom"],
|
||||||
|
42 => ["Die Französische Revolution", "Freiheit, Gleichheit, Brüderlichkeit beendeten 1789 die absolute Monarchie in Frankreich.", "geschichte", "Europa, Politik"],
|
||||||
|
43 => ["Inflation erklärt", "Inflation bezeichnet die kontinuierliche Geldentwertung und den Kaufkraftverlust.", "wirtschaft", "Geld, Finanzen"],
|
||||||
|
44 => ["Die Industrielle Revolution", "Die Erfindung der Dampfmaschine veränderte die Produktion und die Gesellschaft tiefgreifend.", "geschichte", "Industrie, Arbeit"],
|
||||||
|
45 => ["Angebot und Nachfrage", "Dieses fundamentale Marktgesetz bestimmt den Preis von Gütern in einer freien Wirtschaft.", "wirtschaft", "Markt, Preise"],
|
||||||
|
46 => ["Der Buchdruck", "Johannes Gutenbergs Erfindung revolutionierte die Verbreitung von Wissen im Mittelalter.", "geschichte", "Medien, Wissen"],
|
||||||
|
47 => ["Die Entdeckung Amerikas", "Kolumbus' Seereise im Jahr 1492 leitete das Zeitalter der Kolonialisierung ein.", "geschichte", "Entdeckung, Seefahrt"],
|
||||||
|
48 => ["Kryptowährungen", "Bitcoin nutzt Blockchain-Technologie, um dezentralen digitalen Werttransfer zu erlauben.", "wirtschaft", "Blockchain, Finanzen"],
|
||||||
|
49 => ["Das antike Griechenland", "Die Wiege der Demokratie und Philosophie brachte Denker wie Sokrates und Platon hervor.", "geschichte", "Antike, Philosophie"],
|
||||||
|
50 => ["Globalisierung", "Die weltweite Verflechtung in Wirtschaft, Kultur und Politik bringt Chancen und Risiken.", "wirtschaft", "Weltwirtschaft, Handel"],
|
||||||
|
51 => ["Der Dreißigjährige Krieg", "Ein religiöser und politischer Konflikt verwüstete zwischen 1618 und 1648 Mitteleuropa.", "geschichte", "Krieg, Europa"],
|
||||||
|
52 => ["Die Weimarer Republik", "Die erste deutsche Demokratie scheiterte an wirtschaftlichen und politischen Krisen.", "geschichte", "Deutschland, Weimar"],
|
||||||
|
53 => ["Aktien und Börse", "Unternehmen beschaffen sich Kapital durch die Ausgabe von Anteilen an Investoren.", "wirtschaft", "Aktien, Investieren"],
|
||||||
|
54 => ["Das alte Ägypten", "Pharaonen, Pyramiden und Hieroglyphen zeugen von einer hochentwickelten Hochkultur am Nil.", "geschichte", "Ägypten, Antike"],
|
||||||
|
55 => ["Der Kalte Krieg", "Das Wettrüsten zwischen USA und UdSSR prägte die globale Politik der Nachkriegszeit.", "geschichte", "Ost-West, Politik"],
|
||||||
|
56 => ["Zentralbanken und Leitzins", "Durch Zinsänderungen steuern Notenbanken die Geldmenge und bekämpfen Inflation.", "wirtschaft", "Zinsen, Geldpolitik"],
|
||||||
|
57 => ["Die Seidenstraße", "Das historische Netzwerk von Handelsrouten verband über Jahrhunderte Asien und Europa.", "geschichte", "Handel, Asien"],
|
||||||
|
58 => ["Das Mittelalter", "Ritter, Burgen und das Feudalsystem prägten diese tausendjährige Epoche Europas.", "geschichte", "Mittelalter, Feudalismus"],
|
||||||
|
59 => ["Planwirtschaft vs Marktwirtschaft", "Zentrale staatliche Steuerung steht dem freien Spiel der Marktkräfte gegenüber.", "wirtschaft", "Systeme, Wirtschaft"],
|
||||||
|
60 => ["Die Berliner Mauer", "Ihr Bau 1961 zementierte die Teilung Deutschlands, ihr Fall 1989 beendete sie.", "geschichte", "DDR, Wiedervereinigung"],
|
||||||
|
|
||||||
|
// --- ANWENDUNGEN & WEITERE THEMEN (61-100) ---
|
||||||
|
61 => ["Cloud Computing", "Das Auslagern von Rechenleistung in das Internet spart lokale IT-Infrastruktur ein.", "informatik", "Cloud, Web"],
|
||||||
|
62 => ["Responsive Webdesign", "Moderne Webseiten passen ihr Layout dynamisch an Smartphones und Desktops an.", "informatik", "Design, CSS"],
|
||||||
|
63 => ["Der Treibhauseffekt", "Gase in der Atmosphäre verhindern das Entweichen von Wärme ins Weltall.", "physik", "Klima, Umwelt"],
|
||||||
|
64 => ["Mechanik und Kräfte", "Die Newtonschen Axiome beschreiben, wie Kräfte auf Körper wirken und sie bewegen.", "physik", "Newton, Kraft"],
|
||||||
|
65 => ["Die Mendelschen Gesetze", "Die Vererbung von Genen folgt klaren statistischen Wahrscheinlichkeiten.", "biologie", "Genetik, Erbsen"],
|
||||||
|
66 => ["Lichtgeschwindigkeit", "Im Vakuum bewegt sich Licht mit knapp 300.000 Kilometern pro Sekunde.", "physik", "Licht, Relativität"],
|
||||||
|
67 => ["Die Funktion von APIs", "Programmierschnittstellen erlauben den Datenaustausch zwischen verschiedenen Systemen.", "informatik", "API, Schnittstelle"],
|
||||||
|
68 => ["Der Goldstandard", "Ein historischen Währungssystem, bei dem Geld durch echtes Gold gedeckt war.", "wirtschaft", "Gold, Währung"],
|
||||||
|
69 => ["Der Wiener Kongress", "1815 ordneten die europäischen Mächte die Landkarte nach den Napoleonischen Kriegen neu.", "geschichte", "Europa, Diplomatie"],
|
||||||
|
70 => ["Integrierte Schaltkreise", "Mikrochips enthalten Millionen Transistoren auf kleinstem Raum für Logikschaltungen.", "informatik", "Hardware, Chips"],
|
||||||
|
71 => ["Die Magellan-Expedition", "Die erste erfolgreiche Weltumsegelung bewies praktisch die Kugelgestalt der Erde.", "geschichte", "Seefahrt, Erde"],
|
||||||
|
72 => ["Das Internet der Dinge", "Alltagsgegenstände werden vernetzt, um smarte Automatisierungen zu ermöglichen.", "informatik", "IoT, SmartHome"],
|
||||||
|
73 => ["Halbwertszeit", "Die Zeitspanne, in der sich die Hälfte der instabilen Atome radioaktiv abbaut.", "physik", "Atomphysik, Strahlung"],
|
||||||
|
74 => ["Elektromagnetismus", "Die Verknüpfung von elektrischen Strömen und magnetischen Feldern treibt Motoren an.", "physik", "Strom, Magnet"],
|
||||||
|
75 => ["Das Ökosystem Meer", "Ozeane regulieren das Weltklima und bieten Lebensraum für unzählige Arten.", "biologie", "Meer, Ökologie"],
|
||||||
|
76 => ["Einführung in Docker", "Containerisierung isoliert Anwendungen samt Abhängigkeiten für stabilen Betrieb.", "informatik", "Docker, DevOps"],
|
||||||
|
77 => ["Das Römische Recht", "Viele moderne europäische Gesetzbücher basieren auf antiken römischen Rechtsprinzipien.", "geschichte", "Recht, Gesetz"],
|
||||||
|
78 => ["Verhaltensbiologie", "Untersuchung von angeborenen und erlernten Verhaltensweisen bei Mensch und Tier.", "biologie", "Verhalten, Tiere"],
|
||||||
|
79 => ["Verschlüsselung im Alltag", "HTTPS schützt Passwörter und Zahlungsdaten beim Surfen vor dem Mitlesen.", "informatik", "Web, HTTPS"],
|
||||||
|
80 => ["Die Magna Carta", "1215 schränkte dieses Dokument die absolute Macht des englischen Königs ein.", "geschichte", "England, Verfassung"],
|
||||||
|
81 => ["Marktversagen", "Wenn der freie Markt Ressourcen unvollständig verteilt, muss der Staat eingreifen.", "wirtschaft", "Markt, Staat"],
|
||||||
|
82 => ["Optische Linsen", "Konvexe und konkave Linsen brechen Licht für Brillen, Mikroskope und Kameras.", "physik", "Optik, Licht"],
|
||||||
|
83 => ["Die Entstehung der Erde", "Vor rund 4,5 Milliarden Jahren ballte sich kosmischer Staub zu unserem Planeten.", "geographie", "Erde, Kosmos"],
|
||||||
|
84 => ["Grundlagen von JavaScript", "Diese Skriptsprache macht statische Webseiten interaktiv und dynamisch nutzbar.", "informatik", "JS, Webentwicklung"],
|
||||||
|
85 => ["Die industrielle Landwirtschaft", "Moderne Techniken sichern Welternährung, belasten jedoch oft die Umwelt.", "biologie", "Landwirtschaft, Umwelt"],
|
||||||
|
86 => ["Das Schwarze Jahr 1929", "Der New Yorker Börsencrash löste die verheerende Weltwirtschaftskrise aus.", "geschichte", "Krise, Finanzen"],
|
||||||
|
87 => ["Die Evolution des Menschen", "Der Stammbaum des Homo Sapiens entwickelte sich über Millionen Jahre in Afrika.", "biologie", "Mensch, Evolution"],
|
||||||
|
88 => ["Einführung in Linux", "Das Open-Source-Betriebssystem bildet das Rückgrat moderner Server-Infrastrukturen.", "informatik", "Linux, OS"],
|
||||||
|
89 => ["Der Urknall", "Die Urknalltheorie beschreibt den Beginn des Universums aus einer Singularität.", "physik", "Astronomie, Urknall"],
|
||||||
|
90 => ["Das Periodensystem der Elemente", "Dmitri Mendelejew ordnete Elemente logisch nach ihren Atommassen.", "chemie", "Periodensystem, Chemie"],
|
||||||
|
91 => ["Die Hanse", "Ein mächtiger mittelalterlicher Bund von Kaufleuten dominierte den Nordseehandel.", "geschichte", "Handel, Mittelalter"],
|
||||||
|
92 => ["Wirtschaftswachstum", "Die Steigerung des Bruttoinlandsprodukts gilt oft als Indikator für Wohlstand.", "wirtschaft", "BIP, Finanzen"],
|
||||||
|
93 => ["Neuronale Netze", "Diese Strukturen lernen durch mathematische Gewichtung aus riesigen Datenmengen.", "informatik", "KI, Mathematik"],
|
||||||
|
94 => ["Der Erste Weltkrieg", "Der globale Konflikt von 1914 bis 1918 zerstörte das alte europäische Machtgefüge.", "geschichte", "Europa, Krieg"],
|
||||||
|
95 => ["Das Gehirn und Hormone", "Botenstoffe steuern Gefühle, Schlafzyklen und Reaktionen des Körpers.", "biologie", "Medizin, Hormone"],
|
||||||
|
96 => ["SQL Joins erklärt", "Joins verknüpfen Daten aus mehreren Tabellen über gemeinsame Schlüssel.", "informatik", "SQL, Datenbanken"],
|
||||||
|
97 => ["Wellen-Teilchen-Dualismus", "Quantenobjekte zeigen je nach Messaufbau Eigenschaften von Wellen oder Teilchen.", "physik", "Quanten, Licht"],
|
||||||
|
98 => ["Der Absolutismus", "Der Sonnenkönig Ludwig XIV. verkörperte die unbeschränkte Herrschaft des Monarchen.", "geschichte", "Frankreich, Monarchie"],
|
||||||
|
99 => ["Die Funktion von Routern", "Netzwerkgeräte leiten Datenpakete über IP-Adressen an den richtigen Empfänger.", "informatik", "Netzwerk, Internet"],
|
||||||
|
100 => ["Die Entstehung des Geldes", "Vom Tauschhandel über Naturalgeld bis hin zu modernen digitalen Fiat-Währungen.", "wirtschaft", "Geld, Geschichte"]
|
||||||
|
];
|
||||||
|
|
||||||
|
// 10 Dummy-User:
|
||||||
|
$authors = [
|
||||||
|
'max.mustermann@web.de', 'erika.mustermann@web.de', 'john.doe@gmail.com',
|
||||||
|
'jane.doe@gmail.com', 'anna.schmidt@gmx.de', 'thomas.mueller@gmx.de',
|
||||||
|
'sabine.fischer@outlook.com', 'michael.weber@outlook.com', 'julia.wagner@t-online.de',
|
||||||
|
'stefan.becker@t-online.de'
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($dummyArticles as $id => $data) {
|
||||||
|
// Falls der Artikel mit der ID noch nicht existiert, lege ihn an
|
||||||
|
if ($articleManager->getArticle($id) == null) {
|
||||||
|
// Verteilt die 10 Autoren gleichmäßig (ID 1 -> Autor 1, ID 10 -> Autor 10, ID 11 -> Autor 1)
|
||||||
|
$authorEmail = $authors[($id - 1) % 10];
|
||||||
|
|
||||||
|
$blockStructure = [
|
||||||
|
[
|
||||||
|
'type' => 'text',
|
||||||
|
'value' => $data[1] // Der originale Text aus dem Dummy-Array
|
||||||
|
]
|
||||||
|
];
|
||||||
|
$jsonContent = json_encode($blockStructure, JSON_UNESCAPED_UNICODE);
|
||||||
|
|
||||||
|
$articleManager->addArticle(
|
||||||
|
$data[0], // Titel
|
||||||
|
$jsonContent, // Inhalt
|
||||||
|
$authorEmail, // Rotierende Autoren-E-Mail
|
||||||
|
$data[2], // Kategorie
|
||||||
|
$data[3] // Tags
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $articleManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
<?php
|
||||||
|
require_once "Article.php";
|
||||||
|
|
||||||
|
class NotFoundException extends Exception {}
|
||||||
|
class UnauthorizedAccessException extends Exception {}
|
||||||
|
class InternalServerErrorException extends Exception {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Die Klasse beinhaltet alle Methoden für die Operation mit den Artikel-Daten.
|
||||||
|
*
|
||||||
|
* @author Niklas Ortmann
|
||||||
|
*/
|
||||||
|
interface ArticleManagerDAO
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Ein angemeldeter Nutzer erstellt einen neuen Beitrag.
|
||||||
|
* @param $title string Titel des Beitrags
|
||||||
|
* @param $content string Inhalt des Beitrags
|
||||||
|
* @param $author string der Autor des des Beitrages NID
|
||||||
|
* @param $category string Kategorie des Beitrages
|
||||||
|
* @param $tags string optionale Schlagworte für eine bessere Suche
|
||||||
|
*
|
||||||
|
* @throws InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public function addArticle($title, $content, $author, $category, $tags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ändert den gespeicherten Beitrag eines übergebenen Beitrags und eines Autors.
|
||||||
|
* Es wird geprüft, ob der zu änderne Beitrag existiert und ob der übergebene Autor der Autor des originalen
|
||||||
|
* Beitrages ist.
|
||||||
|
* @param $id
|
||||||
|
* @param $article
|
||||||
|
* @param $author
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @throws InternalServerErrorException
|
||||||
|
* @throws NotFoundException
|
||||||
|
* @throws UnauthorizedAccessException
|
||||||
|
*/
|
||||||
|
public function updateArticle($id, $article, $author);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Löscht einen Beitrag aus übergebener ID und dem Nutzer, der die Löschung ausführt.
|
||||||
|
* @param $id
|
||||||
|
* @param $author
|
||||||
|
* @return void
|
||||||
|
* @throws InternalServerErrorException
|
||||||
|
* @throws NotFoundException
|
||||||
|
* @throws UnauthorizedAccessException
|
||||||
|
*/
|
||||||
|
public function deleteArticle($id, $author);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Beitrag aufrufen.
|
||||||
|
* $id ID des Beitrags
|
||||||
|
*
|
||||||
|
* @return Article
|
||||||
|
* @throws InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public function getArticle($id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alle Beiträge aufrufen.
|
||||||
|
*
|
||||||
|
* @throws InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public function getAllArticles();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt alle Beiträge eines Nutzer mit einer gegebenen ID aus.
|
||||||
|
* @param $author
|
||||||
|
* @return Article[]
|
||||||
|
* @throws InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public function getArticlesByAuthor($author);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Durchsucht die vorhandenen Beiträge nach einem bestimmten Suchbegriff.
|
||||||
|
* Die Suche prüft, ob das übergebene Keyword im Titel oder im Inhalt eines Beitrags vorkommt.
|
||||||
|
* (Unabhängig von Groß-und Kleinschreibung)
|
||||||
|
* @param string $keyword Der eingegebene Suchbegriff.
|
||||||
|
* @return array Ein Array von Artikeln ,die dem Suchkriterium entsprechen. Wenn nichts gefunden wird, ein leeres Array.
|
||||||
|
* @throws InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public function search(string $keyword): array;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt alle Beiträge einer gegebenen Kategorie aus.
|
||||||
|
* @param $category
|
||||||
|
* @return mixed
|
||||||
|
* @throws InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public function getArticlesByCategory($category);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registriert oder entfernt ein Like eines Nutzers für einen Beitrag.
|
||||||
|
* Wenn schon geliked -> Unlike
|
||||||
|
* Wenn noch nicht geliked -> Like
|
||||||
|
*
|
||||||
|
* @param int $articleId Die ID des Beitrags
|
||||||
|
* @param string $userId Die ID des Nutzers
|
||||||
|
* @return bool True wenn geliked, False wenn unliked
|
||||||
|
* @throws InternalServerErrorException
|
||||||
|
* @throws NotFoundException
|
||||||
|
*/
|
||||||
|
public function toggleLike(int $articleId, string $userId): bool;
|
||||||
|
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repräsentiert einen Kommentar unter einem Beitrag.
|
||||||
|
*
|
||||||
|
* Ein Kommentar kann entweder ein Hauptkommentar sein
|
||||||
|
* oder eine Antwort auf einen anderen Kommentar.
|
||||||
|
*
|
||||||
|
* @author Caroline Schulte
|
||||||
|
*/
|
||||||
|
class Comment
|
||||||
|
{
|
||||||
|
private int $id;
|
||||||
|
private int $articleId;
|
||||||
|
private ?int $parentCommentId;
|
||||||
|
private string $author;
|
||||||
|
private string $content;
|
||||||
|
private string $created;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erstellt einen neuen Kommentar.
|
||||||
|
*
|
||||||
|
* @param int $id Eindeutige ID des Kommentars
|
||||||
|
* @param int $articleId ID des zugehörigen Beitrags
|
||||||
|
* @param int|null $parentCommentId ID des Eltern-Kommentars oder null
|
||||||
|
* @param string $author Autor des Kommentars
|
||||||
|
* @param string $content Inhalt des Kommentars
|
||||||
|
* @param string $created Erstellungsdatum des Kommentars
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
int $id,
|
||||||
|
int $articleId,
|
||||||
|
?int $parentCommentId,
|
||||||
|
string $author,
|
||||||
|
string $content,
|
||||||
|
string $created
|
||||||
|
) {
|
||||||
|
$this->id = $id;
|
||||||
|
$this->articleId = $articleId;
|
||||||
|
$this->parentCommentId = $parentCommentId;
|
||||||
|
$this->author = $author;
|
||||||
|
$this->content = $content;
|
||||||
|
$this->created = $created;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt die ID des Kommentars zurück.
|
||||||
|
*
|
||||||
|
* @return int Kommentar-ID
|
||||||
|
*/
|
||||||
|
public function getId(): int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt die ID des zugehörigen Beitrags zurück.
|
||||||
|
*
|
||||||
|
* @return int Beitrags-ID
|
||||||
|
*/
|
||||||
|
public function getArticleId(): int
|
||||||
|
{
|
||||||
|
return $this->articleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt die ID des Eltern-Kommentars zurück.
|
||||||
|
*
|
||||||
|
* @return int|null ID des Eltern-Kommentars oder null
|
||||||
|
*/
|
||||||
|
public function getParentCommentId(): ?int
|
||||||
|
{
|
||||||
|
return $this->parentCommentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt zurück, ob der Kommentar eine Antwort ist.
|
||||||
|
*
|
||||||
|
* @return bool true wenn der Kommentar eine Antwort ist, sonst false
|
||||||
|
*/
|
||||||
|
public function isReply(): bool
|
||||||
|
{
|
||||||
|
return $this->parentCommentId !== null && $this->parentCommentId !== 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt den Autor des Kommentars zurück.
|
||||||
|
*
|
||||||
|
* @return string Autor
|
||||||
|
*/
|
||||||
|
public function getAuthor(): string
|
||||||
|
{
|
||||||
|
return $this->author;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt den Inhalt des Kommentars zurück.
|
||||||
|
*
|
||||||
|
* @return string Kommentarinhalt
|
||||||
|
*/
|
||||||
|
public function getContent(): string
|
||||||
|
{
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt das Erstellungsdatum des Kommentars zurück.
|
||||||
|
*
|
||||||
|
* @return string Erstellungsdatum
|
||||||
|
*/
|
||||||
|
public function getCreated(): string
|
||||||
|
{
|
||||||
|
return $this->created;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once "DatabaseCommentManager.php";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zentrale Zugriffsschicht für Kommentare.
|
||||||
|
*
|
||||||
|
* Die Anwendung arbeitet ausschließlich
|
||||||
|
* mit dem CommentManager und kennt die
|
||||||
|
* konkrete Speicherimplementierung nicht.
|
||||||
|
*
|
||||||
|
* @author Caroline Schulte
|
||||||
|
*/
|
||||||
|
class CommentManager
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Gibt die aktive Kommentarverwaltung zurück.
|
||||||
|
*
|
||||||
|
* @return CommentManagerDAO
|
||||||
|
*/
|
||||||
|
public static function getInstance()
|
||||||
|
{
|
||||||
|
return DatabaseCommentManager::getInstance();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once "Comment.php";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schnittstelle für die Verwaltung von Kommentaren.
|
||||||
|
*
|
||||||
|
* Definiert die grundlegenden Methoden zum
|
||||||
|
* Speichern und Laden von Kommentaren.
|
||||||
|
*
|
||||||
|
* @author Caroline Schulte
|
||||||
|
*/
|
||||||
|
interface CommentManagerDAO
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Speichert einen neuen Kommentar zu einem Beitrag.
|
||||||
|
*
|
||||||
|
* Optional kann eine parentCommentId übergeben werden,
|
||||||
|
* wenn der Kommentar eine Antwort auf einen anderen Kommentar ist.
|
||||||
|
*
|
||||||
|
* @param int $articleId ID des Beitrags
|
||||||
|
* @param string $author Autor des Kommentars
|
||||||
|
* @param string $content Inhalt des Kommentars
|
||||||
|
* @param int|null $parentCommentId ID des Eltern-Kommentars oder null
|
||||||
|
*
|
||||||
|
* @return int ID des neu gespeicherten Kommentars
|
||||||
|
*/
|
||||||
|
public function addComment(
|
||||||
|
$articleId,
|
||||||
|
$author,
|
||||||
|
$content,
|
||||||
|
$parentCommentId = null
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt alle Kommentare eines Beitrags zurück.
|
||||||
|
*
|
||||||
|
* @param int $articleId ID des Beitrags
|
||||||
|
*
|
||||||
|
* @return Comment[] Liste der Kommentare
|
||||||
|
*/
|
||||||
|
public function getCommentsByArticle(
|
||||||
|
$articleId
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt alle Kommentare eines Autors zurück.
|
||||||
|
*
|
||||||
|
* @param string $author E-Mail-Adresse des Autors
|
||||||
|
*
|
||||||
|
* @return Comment[] Liste der Kommentare
|
||||||
|
*/
|
||||||
|
public function getCommentsByAuthor($author);
|
||||||
|
}
|
||||||
@@ -0,0 +1,449 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'ArticleManagerDAO.php';
|
||||||
|
require_once 'Article.php';
|
||||||
|
/**
|
||||||
|
* Klasse: Eine SQLLite3 Lösung des ArticleManagerDAO.
|
||||||
|
*
|
||||||
|
* @author Niklas Ortmann
|
||||||
|
*/
|
||||||
|
class DatabaseArticleManager implements ArticleManagerDAO {
|
||||||
|
|
||||||
|
private static $instance = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Konstruktor
|
||||||
|
*
|
||||||
|
* Erstellt die Datenbankverbindung und die Tabelle articles.
|
||||||
|
* @throws InternalServerErrorException
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
if (!file_exists(__DIR__ . '/../../db/articles.db')) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
$db = $this->getConnection();
|
||||||
|
|
||||||
|
// Tabelle für Beiträge
|
||||||
|
$db->exec("
|
||||||
|
CREATE TABLE articles (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
title TEXT,
|
||||||
|
content TEXT,
|
||||||
|
author TEXT,
|
||||||
|
category TEXT,
|
||||||
|
tags TEXT,
|
||||||
|
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);");
|
||||||
|
|
||||||
|
// Tabelle für Likes
|
||||||
|
$db->exec("
|
||||||
|
CREATE TABLE likes (
|
||||||
|
article_id INTEGER,
|
||||||
|
user_id TEXT,
|
||||||
|
PRIMARY KEY (article_id, user_id),
|
||||||
|
FOREIGN KEY (article_id) REFERENCES articles(id) ON DELETE CASCADE
|
||||||
|
);");
|
||||||
|
unset($db);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new InternalServerErrorException($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Baut die Verbindung zur Datenbank auf.
|
||||||
|
* @throws InternalServerErrorException
|
||||||
|
*/
|
||||||
|
private function getConnection()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$user = 'root';
|
||||||
|
$pw = null;
|
||||||
|
$dsn = 'sqlite:' . __DIR__ . '/../../db/articles.db';
|
||||||
|
return new PDO($dsn, $user, $pw);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new InternalServerErrorException($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt die DatabaseArticleManager-Instanz zurück.
|
||||||
|
* @return DatabaseArticleManager
|
||||||
|
*/
|
||||||
|
public static function getInstance()
|
||||||
|
{
|
||||||
|
if (self::$instance == null) {
|
||||||
|
self::$instance = new DatabaseArticleManager();
|
||||||
|
}
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addArticle($title, $content, $author, $category, $tags)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$db = $this->getConnection();
|
||||||
|
|
||||||
|
$sql = "INSERT INTO articles (title, content, author, category, tags)
|
||||||
|
VALUES (:title, :content, :author, :category, :tags);";
|
||||||
|
|
||||||
|
$command = $db->prepare($sql);
|
||||||
|
if (!$command) {
|
||||||
|
throw new InternalServerErrorException("internal_error");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verknüpft die übergebenen Parameter exakt mit den SQL-Platzhaltern
|
||||||
|
$success = $command->execute([
|
||||||
|
":title" => $title,
|
||||||
|
":content" => $content,
|
||||||
|
":author" => $author,
|
||||||
|
":category" => $category,
|
||||||
|
":tags" => $tags
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!$success) {
|
||||||
|
throw new InternalServerErrorException("internal_error");
|
||||||
|
}
|
||||||
|
|
||||||
|
return intval($db->lastInsertId());
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new InternalServerErrorException($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateArticle($id, $article, $author)
|
||||||
|
{
|
||||||
|
if (empty($article)) {
|
||||||
|
throw new InternalServerErrorException("internal_error");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Berechtigungsprüfung analog zur lokalen Implementierung:
|
||||||
|
if ($article->getAuthor() !== $author) {
|
||||||
|
throw new UnauthorizedAccessException("unauthorized_access");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$db = $this->getConnection();
|
||||||
|
|
||||||
|
$sql = "UPDATE articles
|
||||||
|
SET title = :title, content = :content, author = :author, category = :category, tags = :tags
|
||||||
|
WHERE id = :id;";
|
||||||
|
|
||||||
|
$command = $db->prepare($sql);
|
||||||
|
if (!$command) {
|
||||||
|
throw new InternalServerErrorException("internal_error");
|
||||||
|
}
|
||||||
|
|
||||||
|
$success = $command->execute([
|
||||||
|
":id" => $id,
|
||||||
|
":title" => $article->getTitle(),
|
||||||
|
":content" => $article->getContent(),
|
||||||
|
":author" => $author,
|
||||||
|
":category" => $article->getCategory(),
|
||||||
|
":tags" => $article->getTags()
|
||||||
|
]);
|
||||||
|
|
||||||
|
// rowCount() prüft, ob eine Zeile mit dieser ID existierte und geändert werden konnte
|
||||||
|
if (!$success || $command->rowCount() === 0) {
|
||||||
|
// Falls die ID nicht existiert, prüfen wir, ob sie überhaupt da ist
|
||||||
|
if (!$this->getArticle($id)) {
|
||||||
|
throw new NotFoundException("missing_id");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new InternalServerErrorException("internal_error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteArticle($id, $author)
|
||||||
|
{
|
||||||
|
$article = $this->getArticle($id);
|
||||||
|
if (empty($article)) {
|
||||||
|
throw new NotFoundException("not_found_article");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Berechtigungsprüfung:
|
||||||
|
if ($article->getAuthor() !== $author) {
|
||||||
|
throw new UnauthorizedAccessException("unauthorized_access");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$db = $this->getConnection();
|
||||||
|
$sql = "DELETE FROM articles WHERE id = :id;";
|
||||||
|
|
||||||
|
$command = $db->prepare($sql);
|
||||||
|
if (!$command) {
|
||||||
|
throw new InternalServerErrorException("internal_error");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$command->execute([":id" => $id])) {
|
||||||
|
throw new InternalServerErrorException("internal_error");
|
||||||
|
}
|
||||||
|
} catch (PDOException $exc) {
|
||||||
|
throw new InternalServerErrorException("internal_error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getArticle($id)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$db = $this->getConnection();
|
||||||
|
$sql = "SELECT * FROM articles WHERE id = :id;";
|
||||||
|
|
||||||
|
$command = $db->prepare($sql);
|
||||||
|
if (!$command) {
|
||||||
|
throw new InternalServerErrorException("internal_error");
|
||||||
|
}
|
||||||
|
|
||||||
|
$command->execute([":id" => $id]);
|
||||||
|
$row = $command->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if ($row) {
|
||||||
|
$likes = $this->getLikesForArticle(intval($row['id']));
|
||||||
|
|
||||||
|
return new Article(
|
||||||
|
intval($row['id']),
|
||||||
|
$row['title'],
|
||||||
|
$row['content'],
|
||||||
|
$row['author'],
|
||||||
|
$row['category'],
|
||||||
|
$row['tags'],
|
||||||
|
$row['created'],
|
||||||
|
$likes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new InternalServerErrorException("internal_error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllArticles()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$db = $this->getConnection();
|
||||||
|
$sql = "SELECT * FROM articles;";
|
||||||
|
|
||||||
|
$command = $db->query($sql);
|
||||||
|
if (!$command) {
|
||||||
|
throw new InternalServerErrorException("internal_error");
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows = $command->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
$articles = [];
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$articles[] = new Article(
|
||||||
|
intval($row['id']),
|
||||||
|
$row['title'],
|
||||||
|
$row['content'],
|
||||||
|
$row['author'],
|
||||||
|
$row['category'],
|
||||||
|
$row['tags'],
|
||||||
|
$row['created']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $articles;
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new InternalServerErrorException("internal_error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getArticlesByAuthor($author)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$db = $this->getConnection();
|
||||||
|
$sql = "SELECT * FROM articles WHERE author = :author;";
|
||||||
|
|
||||||
|
$command = $db->prepare($sql);
|
||||||
|
if (!$command) {
|
||||||
|
throw new InternalServerErrorException("internal_error");
|
||||||
|
}
|
||||||
|
|
||||||
|
$command->execute([":author" => $author]);
|
||||||
|
$rows = $command->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
$filteredArticles = [];
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$likes = $this->getLikesForArticle(intval($row['id']));
|
||||||
|
|
||||||
|
$filteredArticles[] = new Article(
|
||||||
|
intval($row['id']),
|
||||||
|
$row['title'],
|
||||||
|
$row['content'],
|
||||||
|
$row['author'],
|
||||||
|
$row['category'],
|
||||||
|
$row['tags'],
|
||||||
|
$row['created'],
|
||||||
|
$likes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $filteredArticles;
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new InternalServerErrorException("internal_error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getArticlesByCategory($category)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$db = $this->getConnection();
|
||||||
|
$sql = "SELECT * FROM articles WHERE category = :category;";
|
||||||
|
|
||||||
|
$command = $db->prepare($sql);
|
||||||
|
if (!$command) {
|
||||||
|
throw new InternalServerErrorException("internal_error");
|
||||||
|
}
|
||||||
|
|
||||||
|
$command->execute([":category" => $category]);
|
||||||
|
$rows = $command->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
$filteredArticles = [];
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$likes = $this->getLikesForArticle(intval($row['id']));
|
||||||
|
|
||||||
|
$filteredArticles[] = new Article(
|
||||||
|
intval($row['id']),
|
||||||
|
$row['title'],
|
||||||
|
$row['content'],
|
||||||
|
$row['author'],
|
||||||
|
$row['category'],
|
||||||
|
$row['tags'],
|
||||||
|
$row['created'],
|
||||||
|
$likes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $filteredArticles;
|
||||||
|
} catch (PDOException $exc) {
|
||||||
|
throw new InternalServerErrorException("internal_error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function search(string $keyword): array
|
||||||
|
{
|
||||||
|
$cleankeyword = trim($keyword);
|
||||||
|
|
||||||
|
// Leeren Suchbegriff sofort abfangen
|
||||||
|
if ($cleankeyword === '') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$db = $this->getConnection();
|
||||||
|
|
||||||
|
$sql = "SELECT id, title, content, author, category, tags, created
|
||||||
|
FROM articles
|
||||||
|
WHERE title LIKE :keyword
|
||||||
|
OR content LIKE :keyword;";
|
||||||
|
|
||||||
|
$command = $db->prepare($sql);
|
||||||
|
if (!$command) {
|
||||||
|
throw new InternalServerErrorException("internal_error");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wildcards für die Suche hinzufügen
|
||||||
|
$searchParam = '%' . $cleankeyword . '%';
|
||||||
|
|
||||||
|
$success = $command->execute([
|
||||||
|
":keyword" => $searchParam
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!$success) {
|
||||||
|
throw new InternalServerErrorException("internal_error");
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows = $command->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
$filteredArticles = [];
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$likes = $this->getLikesForArticle(intval($row['id']));
|
||||||
|
|
||||||
|
$filteredArticles[] = new Article(
|
||||||
|
intval($row['id']),
|
||||||
|
$row['title'] ?? '',
|
||||||
|
$row['content'] ?? '',
|
||||||
|
$row['author'] ?? '',
|
||||||
|
$row['category'] ?? '',
|
||||||
|
$row['tags'] ?? '',
|
||||||
|
$row['created'] ?? '',
|
||||||
|
$likes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $filteredArticles;
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new InternalServerErrorException("internal_error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holt alle User-IDs, die einen bestimmten Beitrag geliked haben.
|
||||||
|
*
|
||||||
|
* @return String[] UserIDs
|
||||||
|
* @throws InternalServerErrorException
|
||||||
|
*/
|
||||||
|
private function getLikesForArticle(int $articleId): array
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$db = $this->getConnection();
|
||||||
|
$sql = "SELECT user_id FROM likes WHERE article_id = :article_id;";
|
||||||
|
$command = $db->prepare($sql);
|
||||||
|
$command->execute([':article_id' => $articleId]);
|
||||||
|
|
||||||
|
return $command->fetchAll(PDO::FETCH_COLUMN) ?: [];
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toggleLike(int $articleId, string $userId): bool
|
||||||
|
{
|
||||||
|
// prüfen, ob der Artikel überhaupt existiert
|
||||||
|
$article = $this->getArticle($articleId);
|
||||||
|
if (!$article) {
|
||||||
|
throw new NotFoundException("missing_id");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$db = $this->getConnection();
|
||||||
|
|
||||||
|
// prüfen, ob das Like bereits existiert
|
||||||
|
$checkSql = "SELECT COUNT(*) FROM likes WHERE article_id = :article_id AND user_id = :user_id;";
|
||||||
|
$checkCommand = $db->prepare($checkSql);
|
||||||
|
$checkCommand->execute([
|
||||||
|
':article_id' => $articleId,
|
||||||
|
':user_id' => $userId
|
||||||
|
]);
|
||||||
|
|
||||||
|
$hasLiked = (int)$checkCommand->fetchColumn() > 0;
|
||||||
|
|
||||||
|
if ($hasLiked) {
|
||||||
|
// wenn bereits geliked -> Unlike
|
||||||
|
$deleteSql = "DELETE FROM likes WHERE article_id = :article_id AND user_id = :user_id;";
|
||||||
|
$deleteCommand = $db->prepare($deleteSql);
|
||||||
|
$deleteCommand->execute([
|
||||||
|
':article_id' => $articleId,
|
||||||
|
':user_id' => $userId
|
||||||
|
]);
|
||||||
|
return false; // gibt false zurück, da der Beitrag jetzt nicht mehr geliked ist
|
||||||
|
} else {
|
||||||
|
// wenn noch nicht geliked -> Like
|
||||||
|
$insertSql = "INSERT INTO likes (article_id, user_id) VALUES (:article_id, :user_id);";
|
||||||
|
$insertCommand = $db->prepare($insertSql);
|
||||||
|
$insertCommand->execute([
|
||||||
|
':article_id' => $articleId,
|
||||||
|
':user_id' => $userId
|
||||||
|
]);
|
||||||
|
return true; // gibt true zurück, da der Beitrag jetzt geliked ist
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new InternalServerErrorException("internal_error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,257 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once "CommentManagerDAO.php";
|
||||||
|
require_once "Comment.php";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verwaltet die Speicherung und das Laden von Kommentaren
|
||||||
|
* über eine SQLite-Datenbank.
|
||||||
|
*
|
||||||
|
* @author Caroline Schulte
|
||||||
|
*/
|
||||||
|
class DatabaseCommentManager implements CommentManagerDAO
|
||||||
|
{
|
||||||
|
private static $instance = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erstellt die Kommentartabelle, falls diese noch nicht existiert.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$db = $this->getConnection();
|
||||||
|
|
||||||
|
$db->exec("
|
||||||
|
CREATE TABLE IF NOT EXISTS comments (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
article_id INTEGER NOT NULL,
|
||||||
|
parent_comment_id INTEGER NULL,
|
||||||
|
author TEXT NOT NULL,
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
");
|
||||||
|
|
||||||
|
$columns = $db->query("PRAGMA table_info(comments);")->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
$hasParentColumn = false;
|
||||||
|
|
||||||
|
foreach ($columns as $column) {
|
||||||
|
if ($column["name"] === "parent_comment_id") {
|
||||||
|
$hasParentColumn = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$hasParentColumn) {
|
||||||
|
$db->exec("ALTER TABLE comments ADD COLUMN parent_comment_id INTEGER NULL;");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new RuntimeException("internal_error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Baut die Verbindung zur SQLite-Datenbank auf.
|
||||||
|
*
|
||||||
|
* @return PDO Datenbankverbindung
|
||||||
|
*/
|
||||||
|
private function getConnection()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$dsn = 'sqlite:' . __DIR__ . '/../../db/comments.db';
|
||||||
|
|
||||||
|
$db = new PDO($dsn, null, null);
|
||||||
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
|
||||||
|
return $db;
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new RuntimeException("internal_error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt die Singleton-Instanz zurück.
|
||||||
|
*
|
||||||
|
* @return DatabaseCommentManager
|
||||||
|
*/
|
||||||
|
public static function getInstance()
|
||||||
|
{
|
||||||
|
if (self::$instance === null) {
|
||||||
|
self::$instance = new DatabaseCommentManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Speichert einen neuen Kommentar oder eine Antwort.
|
||||||
|
*
|
||||||
|
* @param int $articleId ID des Beitrags
|
||||||
|
* @param string $author Autor des Kommentars
|
||||||
|
* @param string $content Inhalt des Kommentars
|
||||||
|
* @param int|null $parentCommentId ID des Eltern-Kommentars oder null
|
||||||
|
*
|
||||||
|
* @return int ID des neu gespeicherten Kommentars
|
||||||
|
*/
|
||||||
|
public function addComment(
|
||||||
|
$articleId,
|
||||||
|
$author,
|
||||||
|
$content,
|
||||||
|
$parentCommentId = null
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
$db = $this->getConnection();
|
||||||
|
|
||||||
|
if ($parentCommentId === "" || $parentCommentId === 0 || $parentCommentId === "0") {
|
||||||
|
$parentCommentId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = "
|
||||||
|
INSERT INTO comments (
|
||||||
|
article_id,
|
||||||
|
parent_comment_id,
|
||||||
|
author,
|
||||||
|
content
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
:articleId,
|
||||||
|
:parentCommentId,
|
||||||
|
:author,
|
||||||
|
:content
|
||||||
|
)
|
||||||
|
";
|
||||||
|
|
||||||
|
$command = $db->prepare($sql);
|
||||||
|
|
||||||
|
$command->execute([
|
||||||
|
":articleId" => $articleId,
|
||||||
|
":parentCommentId" => $parentCommentId,
|
||||||
|
":author" => $author,
|
||||||
|
":content" => $content
|
||||||
|
]);
|
||||||
|
|
||||||
|
return intval($db->lastInsertId());
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new RuntimeException("internal_error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lädt alle Kommentare eines Beitrags.
|
||||||
|
*
|
||||||
|
* @param int $articleId ID des Beitrags
|
||||||
|
*
|
||||||
|
* @return Comment[]
|
||||||
|
*/
|
||||||
|
public function getCommentsByArticle($articleId)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$db = $this->getConnection();
|
||||||
|
|
||||||
|
$sql = "
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
article_id,
|
||||||
|
CASE
|
||||||
|
WHEN parent_comment_id IS NULL THEN NULL
|
||||||
|
WHEN parent_comment_id = '' THEN NULL
|
||||||
|
WHEN parent_comment_id = 0 THEN NULL
|
||||||
|
ELSE parent_comment_id
|
||||||
|
END AS parent_comment_id,
|
||||||
|
author,
|
||||||
|
content,
|
||||||
|
created
|
||||||
|
FROM comments
|
||||||
|
WHERE article_id = :articleId
|
||||||
|
ORDER BY created ASC
|
||||||
|
";
|
||||||
|
|
||||||
|
$command = $db->prepare($sql);
|
||||||
|
$command->execute([":articleId" => $articleId]);
|
||||||
|
|
||||||
|
return $this->mapRowsToComments($command);
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new RuntimeException("internal_error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lädt alle Kommentare eines Autors.
|
||||||
|
*
|
||||||
|
* @param string $author E-Mail-Adresse des Autors
|
||||||
|
*
|
||||||
|
* @return Comment[]
|
||||||
|
*/
|
||||||
|
public function getCommentsByAuthor($author)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$db = $this->getConnection();
|
||||||
|
|
||||||
|
$sql = "
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
article_id,
|
||||||
|
CASE
|
||||||
|
WHEN parent_comment_id IS NULL THEN NULL
|
||||||
|
WHEN parent_comment_id = '' THEN NULL
|
||||||
|
WHEN parent_comment_id = 0 THEN NULL
|
||||||
|
ELSE parent_comment_id
|
||||||
|
END AS parent_comment_id,
|
||||||
|
author,
|
||||||
|
content,
|
||||||
|
created
|
||||||
|
FROM comments
|
||||||
|
WHERE author = :author
|
||||||
|
ORDER BY created DESC
|
||||||
|
";
|
||||||
|
|
||||||
|
$command = $db->prepare($sql);
|
||||||
|
$command->execute([":author" => $author]);
|
||||||
|
|
||||||
|
return $this->mapRowsToComments($command);
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new RuntimeException("internal_error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wandelt Datenbankzeilen in Comment-Objekte um.
|
||||||
|
*
|
||||||
|
* @param PDOStatement $command Ausgeführtes Statement
|
||||||
|
*
|
||||||
|
* @return Comment[]
|
||||||
|
*/
|
||||||
|
private function mapRowsToComments($command)
|
||||||
|
{
|
||||||
|
$comments = [];
|
||||||
|
|
||||||
|
while ($row = $command->fetch(PDO::FETCH_ASSOC)) {
|
||||||
|
$parentCommentId = null;
|
||||||
|
|
||||||
|
if (
|
||||||
|
isset($row["parent_comment_id"])
|
||||||
|
&& $row["parent_comment_id"] !== null
|
||||||
|
&& $row["parent_comment_id"] !== ""
|
||||||
|
&& intval($row["parent_comment_id"]) !== 0
|
||||||
|
) {
|
||||||
|
$parentCommentId = intval($row["parent_comment_id"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$comments[] = new Comment(
|
||||||
|
intval($row["id"]),
|
||||||
|
intval($row["article_id"]),
|
||||||
|
$parentCommentId,
|
||||||
|
$row["author"],
|
||||||
|
$row["content"],
|
||||||
|
$row["created"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $comments;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,261 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once "UserManagerDAO.php";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Klasse für den Zugriff auf Benutzerdaten über eine SQLite-Datenbank.
|
||||||
|
*
|
||||||
|
* Diese Klasse verwendet PDO, Prepared Statements und speichert
|
||||||
|
* Benutzerdaten in der Datei db/users.db.
|
||||||
|
*/
|
||||||
|
class DatabaseUserManager implements UserManagerDAO {
|
||||||
|
|
||||||
|
private static $instance = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Konstruktor.
|
||||||
|
*
|
||||||
|
* Erstellt die Benutzerdatenbank und die Tabelle users,
|
||||||
|
* falls diese noch nicht existieren.
|
||||||
|
*
|
||||||
|
* @throws RuntimeException wenn die Datenbank nicht erstellt werden kann
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$db = $this->getConnection();
|
||||||
|
|
||||||
|
$db->exec("
|
||||||
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
email TEXT PRIMARY KEY,
|
||||||
|
vorname TEXT NOT NULL,
|
||||||
|
nachname TEXT NOT NULL,
|
||||||
|
password TEXT NOT NULL
|
||||||
|
);
|
||||||
|
");
|
||||||
|
|
||||||
|
unset($db);
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new RuntimeException("Benutzerdatenbank konnte nicht erstellt werden.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Baut eine Verbindung zur SQLite-Datenbank auf.
|
||||||
|
*
|
||||||
|
* @return PDO Datenbankverbindung
|
||||||
|
*
|
||||||
|
* @throws RuntimeException wenn keine Verbindung hergestellt werden kann
|
||||||
|
*/
|
||||||
|
private function getConnection()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$dsn = 'sqlite:' . __DIR__ . '/../../db/users.db';
|
||||||
|
|
||||||
|
$db = new PDO($dsn, null, null);
|
||||||
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
|
||||||
|
return $db;
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new RuntimeException("Verbindung zur Benutzerdatenbank fehlgeschlagen.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt die Singleton-Instanz des DatabaseUserManagers zurück.
|
||||||
|
*
|
||||||
|
* @return DatabaseUserManager Instanz des DatabaseUserManagers
|
||||||
|
*/
|
||||||
|
public static function getInstance()
|
||||||
|
{
|
||||||
|
if (self::$instance == null) {
|
||||||
|
self::$instance = new DatabaseUserManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sucht einen Benutzer anhand seiner E-Mail-Adresse.
|
||||||
|
*
|
||||||
|
* @param string $email E-Mail-Adresse des Benutzers
|
||||||
|
*
|
||||||
|
* @return array|null Benutzerdaten als Array oder null,
|
||||||
|
* wenn kein Benutzer gefunden wurde
|
||||||
|
*
|
||||||
|
* @throws RuntimeException wenn der Benutzer nicht geladen werden kann
|
||||||
|
*/
|
||||||
|
public function findUser($email)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$db = $this->getConnection();
|
||||||
|
|
||||||
|
$sql = "SELECT * FROM users WHERE email = :email";
|
||||||
|
$command = $db->prepare($sql);
|
||||||
|
|
||||||
|
$command->execute([
|
||||||
|
":email" => $email
|
||||||
|
]);
|
||||||
|
|
||||||
|
$user = $command->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
return $user ?: null;
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new RuntimeException("Benutzer konnte nicht geladen werden.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fügt einen neuen Benutzer in die Datenbank ein.
|
||||||
|
*
|
||||||
|
* Die Speicherung erfolgt innerhalb einer Transaktion.
|
||||||
|
* Doppelte E-Mail-Adressen werden durch den Primary Key verhindert.
|
||||||
|
*
|
||||||
|
* @param string $email E-Mail-Adresse des Benutzers
|
||||||
|
* @param string $vorname Vorname des Benutzers
|
||||||
|
* @param string $nachname Nachname des Benutzers
|
||||||
|
* @param string $password Passwort-Hash des Benutzers
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @throws InvalidArgumentException wenn die E-Mail-Adresse bereits verwendet wird
|
||||||
|
* @throws RuntimeException wenn der Benutzer nicht gespeichert werden kann
|
||||||
|
*/
|
||||||
|
public function addUser($email, $vorname, $nachname, $password)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$db = $this->getConnection();
|
||||||
|
$db->beginTransaction();
|
||||||
|
|
||||||
|
$sql = "
|
||||||
|
INSERT INTO users (email, vorname, nachname, password)
|
||||||
|
VALUES (:email, :vorname, :nachname, :password)
|
||||||
|
";
|
||||||
|
|
||||||
|
$command = $db->prepare($sql);
|
||||||
|
|
||||||
|
$command->execute([
|
||||||
|
":email" => $email,
|
||||||
|
":vorname" => $vorname,
|
||||||
|
":nachname" => $nachname,
|
||||||
|
":password" => $password
|
||||||
|
]);
|
||||||
|
|
||||||
|
$db->commit();
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
if (isset($db) && $db->inTransaction()) {
|
||||||
|
$db->rollBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($e->getCode() === "23000") {
|
||||||
|
throw new InvalidArgumentException("Diese E-Mail-Adresse wird bereits verwendet.");
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RuntimeException("Benutzer konnte nicht gespeichert werden.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aktualisiert die Daten eines bestehenden Benutzers.
|
||||||
|
*
|
||||||
|
* Optional kann zusätzlich das Passwort geändert werden.
|
||||||
|
* Wenn kein neues Passwort übergeben wird, bleibt das alte Passwort erhalten.
|
||||||
|
*
|
||||||
|
* @param string $oldEmail Aktuelle E-Mail-Adresse des Benutzers
|
||||||
|
* @param string $newEmail Neue E-Mail-Adresse des Benutzers
|
||||||
|
* @param string $vorname Neuer Vorname des Benutzers
|
||||||
|
* @param string $nachname Neuer Nachname des Benutzers
|
||||||
|
* @param string|null $password Neues Passwort oder null
|
||||||
|
*
|
||||||
|
* @return bool true, wenn der Benutzer aktualisiert wurde,
|
||||||
|
* sonst false
|
||||||
|
*
|
||||||
|
* @throws InvalidArgumentException wenn die neue E-Mail-Adresse bereits verwendet wird
|
||||||
|
* @throws RuntimeException wenn der Benutzer nicht aktualisiert werden kann
|
||||||
|
*/
|
||||||
|
public function updateUser($oldEmail, $newEmail, $vorname, $nachname, $password = null)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$db = $this->getConnection();
|
||||||
|
|
||||||
|
if (!empty($password)) {
|
||||||
|
$sql = "
|
||||||
|
UPDATE users
|
||||||
|
SET email = :newEmail,
|
||||||
|
vorname = :vorname,
|
||||||
|
nachname = :nachname,
|
||||||
|
password = :password
|
||||||
|
WHERE email = :oldEmail
|
||||||
|
";
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
":newEmail" => $newEmail,
|
||||||
|
":vorname" => $vorname,
|
||||||
|
":nachname" => $nachname,
|
||||||
|
":password" => password_hash($password, PASSWORD_DEFAULT),
|
||||||
|
":oldEmail" => $oldEmail
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$sql = "
|
||||||
|
UPDATE users
|
||||||
|
SET email = :newEmail,
|
||||||
|
vorname = :vorname,
|
||||||
|
nachname = :nachname
|
||||||
|
WHERE email = :oldEmail
|
||||||
|
";
|
||||||
|
|
||||||
|
$params = [
|
||||||
|
":newEmail" => $newEmail,
|
||||||
|
":vorname" => $vorname,
|
||||||
|
":nachname" => $nachname,
|
||||||
|
":oldEmail" => $oldEmail
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$command = $db->prepare($sql);
|
||||||
|
$command->execute($params);
|
||||||
|
|
||||||
|
return $command->rowCount() > 0;
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
if ($e->getCode() === "23000") {
|
||||||
|
throw new InvalidArgumentException("Diese E-Mail-Adresse wird bereits verwendet.");
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RuntimeException("Benutzer konnte nicht aktualisiert werden.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Löscht einen Benutzer anhand seiner E-Mail-Adresse.
|
||||||
|
*
|
||||||
|
* @param string $email E-Mail-Adresse des zu löschenden Benutzers
|
||||||
|
*
|
||||||
|
* @return bool true, wenn der Benutzer gelöscht wurde,
|
||||||
|
* sonst false
|
||||||
|
*
|
||||||
|
* @throws RuntimeException wenn der Benutzer nicht gelöscht werden kann
|
||||||
|
*/
|
||||||
|
public function deleteUser($email)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$db = $this->getConnection();
|
||||||
|
|
||||||
|
$sql = "DELETE FROM users WHERE email = :email";
|
||||||
|
$command = $db->prepare($sql);
|
||||||
|
|
||||||
|
$command->execute([
|
||||||
|
":email" => $email
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $command->rowCount() > 0;
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new RuntimeException("Benutzer konnte nicht gelöscht werden.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,299 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'ArticleManagerDAO.php';
|
||||||
|
require_once 'Article.php';
|
||||||
|
/**
|
||||||
|
* Klasse: Eine lokale Lösung des ArticleManagerDAO.
|
||||||
|
* Schreibt zunächst Daten lokal in das Verzeichnis und kann diese wieder auslesen.
|
||||||
|
*
|
||||||
|
* @author Niklas Ortmann
|
||||||
|
*/
|
||||||
|
class LocalArticleManager implements ArticleManagerDAO {
|
||||||
|
|
||||||
|
private $file;
|
||||||
|
private static $instance = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Konstruktor
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->file = __DIR__ . '/../../data/articles.json';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt die LocalArticleManager-Instanz zurück.
|
||||||
|
* @return LocalArticleManager
|
||||||
|
*/
|
||||||
|
public static function getInstance()
|
||||||
|
{
|
||||||
|
if (self::$instance == null) {
|
||||||
|
self::$instance = new LocalArticleManager();
|
||||||
|
}
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Speichert alle Artikel/Beiträge in der Datei.
|
||||||
|
* @param $articles
|
||||||
|
* @return void
|
||||||
|
* @throws InternalServerErrorException
|
||||||
|
*/
|
||||||
|
private function saveArticle($articles)
|
||||||
|
{
|
||||||
|
try{
|
||||||
|
file_put_contents(
|
||||||
|
$this->file,
|
||||||
|
json_encode($articles, JSON_PRETTY_PRINT)
|
||||||
|
);
|
||||||
|
}catch (Exception $e){
|
||||||
|
throw new InternalServerErrorException($e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addArticle($title, $content, $author, $category, $tags)
|
||||||
|
{
|
||||||
|
$articles = $this->getAllArticles();
|
||||||
|
|
||||||
|
$articles[] = [
|
||||||
|
"id" => count($articles)+1,
|
||||||
|
"title" => $title,
|
||||||
|
"content" => $content,
|
||||||
|
"author" => $author,
|
||||||
|
"category" => $category,
|
||||||
|
"tags" => $tags,
|
||||||
|
"creationDate" => date("Y-m-d H:i:s"),
|
||||||
|
"likes" => []
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->saveArticle($articles);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateArticle($id, $article, $author)
|
||||||
|
{
|
||||||
|
if (empty($article)) {
|
||||||
|
throw new NotFoundException("not_found_article");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Berechtigungsprüfung:
|
||||||
|
if ($article->getAuthor() !== $author) {
|
||||||
|
throw new UnauthorizedAccessException("unauthorized_access");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Beitrag aktualisieren:
|
||||||
|
$articles = $this->getAllArticles();
|
||||||
|
$updated = false;
|
||||||
|
|
||||||
|
foreach ($articles as $index => $storedArticle) {
|
||||||
|
if (isset($storedArticle['id']) && $storedArticle['id'] == $id) {
|
||||||
|
$articles[$index] = [
|
||||||
|
"id" => $id,
|
||||||
|
"title" => $article->getTitle(),
|
||||||
|
"content" => $article->getContent(),
|
||||||
|
"author" => $author,
|
||||||
|
"category" => $article->getCategory(),
|
||||||
|
"tags" => $article->getTags(),
|
||||||
|
"creationDate" => $article->getCreationDate(),
|
||||||
|
"likes" => $storedArticle['likes'] ?? []
|
||||||
|
];
|
||||||
|
$updated = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nur speichern, wenn Beitrag geändert wurde:
|
||||||
|
if ($updated) {
|
||||||
|
$this->saveArticle($articles);
|
||||||
|
} else {
|
||||||
|
throw new NotFoundException("missing_id");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteArticle($id, $author)
|
||||||
|
{
|
||||||
|
$article = getArticle($id);
|
||||||
|
if (empty($article)) {
|
||||||
|
throw new NotFoundException("not_found_article");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Berechtigungsprüfung:
|
||||||
|
if ($article->getAuthor() !== $author) {
|
||||||
|
throw new UnauthorizedAccessException("unauthorized_access");
|
||||||
|
}
|
||||||
|
|
||||||
|
$articles = $this->getAllArticles();
|
||||||
|
$articleFound = false;
|
||||||
|
|
||||||
|
foreach ($articles as $index => $article) {
|
||||||
|
if (isset($article['id']) && $article['id'] == $id) {
|
||||||
|
unset($articles[$index]);
|
||||||
|
$articleFound = true;
|
||||||
|
break; // Schleife abbrechen, da die ID eindeutig ist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($articleFound) {
|
||||||
|
// array_values stellt sicher, dass die Array-Keys wieder fortlaufend bei 0 beginnen
|
||||||
|
$this->saveArticle(array_values($articles));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getArticle($id)
|
||||||
|
{
|
||||||
|
$articles = $this->getAllArticles();
|
||||||
|
|
||||||
|
foreach ($articles as $article) {
|
||||||
|
if (isset($article['id']) && $article['id'] == $id) {
|
||||||
|
$likes = isset($article['likes']) && is_array($article['likes']) ? $article['likes'] : [];
|
||||||
|
return new Article(
|
||||||
|
intval($article['id']),
|
||||||
|
$article['title'],
|
||||||
|
$article['content'],
|
||||||
|
$article['author'],
|
||||||
|
$article['category'],
|
||||||
|
$article['tags'],
|
||||||
|
$article['creationDate'],
|
||||||
|
$likes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllArticles(): array
|
||||||
|
{
|
||||||
|
if (!file_exists($this->file)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$json = file_get_contents($this->file);
|
||||||
|
$articles = json_decode($json, true);
|
||||||
|
|
||||||
|
return is_array($articles) ? $articles : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getArticlesByAuthor($author)
|
||||||
|
{
|
||||||
|
$articles = $this->getAllArticles();
|
||||||
|
$filteredArticles = [];
|
||||||
|
|
||||||
|
foreach ($articles as $article) {
|
||||||
|
if (isset($article['author']) && $article['author'] == $author) {
|
||||||
|
$likes = isset($article['likes']) && is_array($article['likes']) ? $article['likes'] : [];
|
||||||
|
$filteredArticles[] = new Article(
|
||||||
|
intval($article['id']),
|
||||||
|
$article['title'],
|
||||||
|
$article['content'],
|
||||||
|
$article['author'],
|
||||||
|
$article['category'],
|
||||||
|
$article['tags'],
|
||||||
|
$article['creationDate'],
|
||||||
|
$likes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $filteredArticles;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function search(string $keyword): array
|
||||||
|
{
|
||||||
|
$articles = $this->getAllArticles();
|
||||||
|
$filteredArticles = [];
|
||||||
|
|
||||||
|
if (!is_array($articles)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$cleanKeyword = strtolower(trim($keyword));
|
||||||
|
|
||||||
|
foreach ($articles as $article) {
|
||||||
|
$title = isset($article['title']) ? strtolower((string)$article['title']) : '';
|
||||||
|
$content = isset($article['content']) ? strtolower((string)$article['content']) : '';
|
||||||
|
|
||||||
|
if (($cleanKeyword !== '' && strpos($title, $cleanKeyword) !== false) ||
|
||||||
|
($cleanKeyword !== '' && strpos($content, $cleanKeyword) !== false)) {
|
||||||
|
|
||||||
|
$likes = isset($article['likes']) && is_array($article['likes']) ? $article['likes'] : [];
|
||||||
|
$filteredArticles[] = new Article(
|
||||||
|
intval($article['id'] ?? 0),
|
||||||
|
$article['title'] ?? '',
|
||||||
|
$article['content'] ?? '',
|
||||||
|
$article['author'] ?? '',
|
||||||
|
$article['category'] ?? '',
|
||||||
|
$article['tags'] ?? '',
|
||||||
|
$article['creationDate'] ?? '',
|
||||||
|
$likes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $filteredArticles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getArticlesByCategory($category)
|
||||||
|
{
|
||||||
|
$articles = $this->getAllArticles();
|
||||||
|
$filteredArticles = [];
|
||||||
|
|
||||||
|
foreach ($articles as $article) {
|
||||||
|
if (isset($article['category']) && $article['category'] == $category) {
|
||||||
|
$likes = isset($article['likes']) && is_array($article['likes']) ? $article['likes'] : [];
|
||||||
|
$filteredArticles[] = new Article(
|
||||||
|
intval($article['id']),
|
||||||
|
$article['title'],
|
||||||
|
$article['content'],
|
||||||
|
$article['author'],
|
||||||
|
$article['category'],
|
||||||
|
$article['tags'],
|
||||||
|
$article['creationDate'],
|
||||||
|
$likes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $filteredArticles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toggleLike(int $articleId, string $userId): bool
|
||||||
|
{
|
||||||
|
$articles = $this->getAllArticles();
|
||||||
|
$articleFound = false;
|
||||||
|
$isLikedNow = false;
|
||||||
|
|
||||||
|
foreach ($articles as $index => $article) {
|
||||||
|
if (isset($article['id']) && $article['id'] == $articleId) {
|
||||||
|
$articleFound = true;
|
||||||
|
|
||||||
|
// Likes-Array initialisieren, falls nicht vorhanden
|
||||||
|
if (!isset($articles[$index]['likes']) || !is_array($articles[$index]['likes'])) {
|
||||||
|
$articles[$index]['likes'] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$likeIndex = array_search($userId, $articles[$index]['likes']);
|
||||||
|
|
||||||
|
if ($likeIndex !== false) {
|
||||||
|
// Bereits geliked -> Unlike
|
||||||
|
unset($articles[$index]['likes'][$likeIndex]);
|
||||||
|
// Array-Keys neu indizieren, damit JSON sauber bleibt
|
||||||
|
$articles[$index]['likes'] = array_values($articles[$index]['likes']);
|
||||||
|
$isLikedNow = false;
|
||||||
|
} else {
|
||||||
|
// Noch nicht geliked -> Like (User-ID hinzufügen)
|
||||||
|
$articles[$index]['likes'][] = $userId;
|
||||||
|
$isLikedNow = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$articleFound) {
|
||||||
|
throw new NotFoundException("missing_id");
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->saveArticle($articles);
|
||||||
|
return $isLikedNow;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once "UserManagerDAO.php";
|
||||||
|
|
||||||
|
class LocalUserManager implements UserManagerDAO {
|
||||||
|
|
||||||
|
private string $file = "data/users.json";
|
||||||
|
|
||||||
|
public static function getInstance(){
|
||||||
|
// TODO: implement the getIsntance method.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lädt alle Benutzer aus der JSON-Datei.
|
||||||
|
*
|
||||||
|
* @return array Liste aller Benutzer
|
||||||
|
* @throws RuntimeException wenn die Datei nicht gelesen werden kann
|
||||||
|
* oder fehlerhafte JSON-Daten enthält
|
||||||
|
*/
|
||||||
|
private function loadUsers() {
|
||||||
|
if (!file_exists($this->file)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$json = file_get_contents($this->file);
|
||||||
|
|
||||||
|
if ($json === false) {
|
||||||
|
throw new RuntimeException("Benutzerdaten konnten nicht gelesen werden.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$users = json_decode($json, true);
|
||||||
|
|
||||||
|
if ($users === null && json_last_error() !== JSON_ERROR_NONE) {
|
||||||
|
throw new RuntimeException("Benutzerdaten sind fehlerhaft.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_array($users) ? $users : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Speichert alle Benutzer in die JSON-Datei.
|
||||||
|
*
|
||||||
|
* @param array $users Liste aller Benutzer
|
||||||
|
* @return void
|
||||||
|
* @throws RuntimeException wenn die Daten nicht gespeichert werden können
|
||||||
|
*/
|
||||||
|
private function saveUsers($users) {
|
||||||
|
$json = json_encode($users, JSON_PRETTY_PRINT);
|
||||||
|
|
||||||
|
if ($json === false) {
|
||||||
|
throw new RuntimeException("Benutzerdaten konnten nicht umgewandelt werden.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = file_put_contents($this->file, $json);
|
||||||
|
|
||||||
|
if ($result === false) {
|
||||||
|
throw new RuntimeException("Benutzerdaten konnten nicht gespeichert werden.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sucht einen Benutzer anhand seiner E-Mail-Adresse.
|
||||||
|
*
|
||||||
|
* @param string $email E-Mail-Adresse des gesuchten Benutzers
|
||||||
|
* @return array|null Benutzerdaten oder null, wenn kein Benutzer gefunden wurde
|
||||||
|
* @throws RuntimeException wenn die Benutzerdaten nicht gelesen werden können
|
||||||
|
*/
|
||||||
|
public function findUser($email) {
|
||||||
|
$users = $this->loadUsers();
|
||||||
|
|
||||||
|
foreach ($users as $user) {
|
||||||
|
if (isset($user["email"]) && $user["email"] === $email) {
|
||||||
|
|
||||||
|
if (!isset($user["vorname"]) && isset($user["username"])) {
|
||||||
|
$nameParts = explode(" ", $user["username"], 2);
|
||||||
|
$user["vorname"] = $nameParts[0] ?? "";
|
||||||
|
$user["nachname"] = $nameParts[1] ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fügt einen neuen Benutzer hinzu.
|
||||||
|
*
|
||||||
|
* @param string $email E-Mail-Adresse
|
||||||
|
* @param string $vorname Vorname
|
||||||
|
* @param string $nachname Nachname
|
||||||
|
* @param string $password Passwort-Hash
|
||||||
|
* @return void
|
||||||
|
* @throws InvalidArgumentException wenn die E-Mail-Adresse bereits verwendet wird
|
||||||
|
* @throws RuntimeException wenn die Benutzerdaten nicht gelesen oder gespeichert werden können
|
||||||
|
*/
|
||||||
|
public function addUser($email, $vorname, $nachname, $password) {
|
||||||
|
$users = $this->loadUsers();
|
||||||
|
|
||||||
|
foreach ($users as $user) {
|
||||||
|
if (isset($user["email"]) && $user["email"] === $email) {
|
||||||
|
throw new InvalidArgumentException("Diese E-Mail-Adresse wird bereits verwendet.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$users[] = [
|
||||||
|
"email" => $email,
|
||||||
|
"vorname" => $vorname,
|
||||||
|
"nachname" => $nachname,
|
||||||
|
"password" => $password
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->saveUsers($users);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aktualisiert einen bestehenden Benutzer.
|
||||||
|
*
|
||||||
|
* @param string $oldEmail Alte E-Mail-Adresse
|
||||||
|
* @param string $newEmail Neue E-Mail-Adresse
|
||||||
|
* @param string $vorname Neuer Vorname
|
||||||
|
* @param string $nachname Neuer Nachname
|
||||||
|
* @param string|null $password Neues Passwort oder null
|
||||||
|
* @return bool true, wenn der Benutzer aktualisiert wurde, sonst false
|
||||||
|
* @throws InvalidArgumentException wenn die neue E-Mail-Adresse bereits verwendet wird
|
||||||
|
* @throws RuntimeException wenn die Benutzerdaten nicht gelesen oder gespeichert werden können
|
||||||
|
*/
|
||||||
|
public function updateUser($oldEmail, $newEmail, $vorname, $nachname, $password = null) {
|
||||||
|
$users = $this->loadUsers();
|
||||||
|
|
||||||
|
foreach ($users as $user) {
|
||||||
|
if (
|
||||||
|
isset($user["email"])
|
||||||
|
&& $user["email"] === $newEmail
|
||||||
|
&& $newEmail !== $oldEmail
|
||||||
|
) {
|
||||||
|
throw new InvalidArgumentException("Diese E-Mail-Adresse wird bereits verwendet.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($users as $i => $user) {
|
||||||
|
if (isset($user["email"]) && $user["email"] === $oldEmail) {
|
||||||
|
$users[$i]["email"] = $newEmail;
|
||||||
|
$users[$i]["vorname"] = $vorname;
|
||||||
|
$users[$i]["nachname"] = $nachname;
|
||||||
|
|
||||||
|
unset($users[$i]["username"]);
|
||||||
|
|
||||||
|
if (!empty($password)) {
|
||||||
|
$users[$i]["password"] = password_hash($password, PASSWORD_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->saveUsers($users);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Löscht einen Benutzer anhand seiner E-Mail-Adresse.
|
||||||
|
*
|
||||||
|
* TODO: wenn ein Benutzer gelöscht wird, sollten dann auch seine Beiträge gelöscht werden?
|
||||||
|
*
|
||||||
|
* @param string $email E-Mail-Adresse des zu löschenden Benutzers
|
||||||
|
* @return bool true, wenn der Benutzer gelöscht wurde, sonst false
|
||||||
|
* @throws RuntimeException wenn die Benutzerdaten nicht gelesen oder gespeichert werden können
|
||||||
|
*/
|
||||||
|
public function deleteUser($email) {
|
||||||
|
$users = $this->loadUsers();
|
||||||
|
|
||||||
|
foreach ($users as $i => $user) {
|
||||||
|
if (isset($user["email"]) && $user["email"] === $email) {
|
||||||
|
unset($users[$i]);
|
||||||
|
$users = array_values($users);
|
||||||
|
$this->saveUsers($users);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once "LocalUserManager.php";
|
||||||
|
require_once "DatabaseUserManager.php";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zentrale Klasse für den Zugriff auf Benutzerdaten.
|
||||||
|
*
|
||||||
|
* Hier kann zwischen lokaler Speicherung und Datenbankspeicherung
|
||||||
|
* gewechselt werden.
|
||||||
|
*/
|
||||||
|
class UserManager {
|
||||||
|
|
||||||
|
public static function getInstance() {
|
||||||
|
$userManager = DatabaseUserManager::getInstance();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dummy-User anlegen, falls sie noch nicht existieren.
|
||||||
|
* Passwort für alle User: test12345
|
||||||
|
*/
|
||||||
|
$dummyUsers = [
|
||||||
|
['email' => 'max.mustermann@web.de', 'vorname' => 'Max', 'nachname' => 'Mustermann'],
|
||||||
|
['email' => 'erika.mustermann@web.de', 'vorname' => 'Erika', 'nachname' => 'Mustermann'],
|
||||||
|
['email' => 'john.doe@gmail.com', 'vorname' => 'John', 'nachname' => 'Doe'],
|
||||||
|
['email' => 'jane.doe@gmail.com', 'vorname' => 'Jane', 'nachname' => 'Doe'],
|
||||||
|
['email' => 'anna.schmidt@gmx.de', 'vorname' => 'Anna', 'nachname' => 'Schmidt'],
|
||||||
|
['email' => 'thomas.mueller@gmx.de', 'vorname' => 'Thomas', 'nachname' => 'Müller'],
|
||||||
|
['email' => 'sabine.fischer@outlook.com', 'vorname' => 'Sabine', 'nachname' => 'Fischer'],
|
||||||
|
['email' => 'michael.weber@outlook.com', 'vorname' => 'Michael', 'nachname' => 'Weber'],
|
||||||
|
['email' => 'julia.wagner@t-online.de', 'vorname' => 'Julia', 'nachname' => 'Wagner'],
|
||||||
|
['email' => 'stefan.becker@t-online.de', 'vorname' => 'Stefan', 'nachname' => 'Becker']
|
||||||
|
];
|
||||||
|
|
||||||
|
$passwordHash = password_hash("test12345", PASSWORD_DEFAULT);
|
||||||
|
|
||||||
|
foreach ($dummyUsers as $user) {
|
||||||
|
if ($userManager->findUser($user['email']) == null) {
|
||||||
|
$userManager->addUser(
|
||||||
|
$user['email'],
|
||||||
|
$user['vorname'],
|
||||||
|
$user['nachname'],
|
||||||
|
$passwordHash
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $userManager;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface für den Zugriff auf Benutzerdaten.
|
||||||
|
*
|
||||||
|
* Definiert die Methoden, die jede UserDAO-Implementierung
|
||||||
|
* bereitstellen muss.
|
||||||
|
*/
|
||||||
|
interface UserManagerDAO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sucht einen Benutzer anhand seiner E-Mail-Adresse.
|
||||||
|
*
|
||||||
|
* Funktion:
|
||||||
|
* Liefert die gespeicherten Benutzerdaten zu einer E-Mail-Adresse.
|
||||||
|
*
|
||||||
|
* Eingabe:
|
||||||
|
* @param string $email E-Mail-Adresse des gesuchten Benutzers
|
||||||
|
*
|
||||||
|
* Ausgabe:
|
||||||
|
* @return array|null Benutzerdaten als Array oder null
|
||||||
|
*
|
||||||
|
* Mögliche Fehler:
|
||||||
|
* @throws RuntimeException wenn die Benutzerdaten nicht gelesen werden können
|
||||||
|
*/
|
||||||
|
public function findUser($email);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fügt einen neuen Benutzer hinzu.
|
||||||
|
*
|
||||||
|
* Funktion:
|
||||||
|
* Erstellt einen neuen Benutzereintrag und speichert ihn
|
||||||
|
* in der jeweiligen Datenquelle.
|
||||||
|
*
|
||||||
|
* Eingabe:
|
||||||
|
* @param string $email E-Mail-Adresse des Benutzers
|
||||||
|
* @param string $vorname Vorname des Benutzers
|
||||||
|
* @param string $nachname Nachname des Benutzers
|
||||||
|
* @param string $password Passwort des Benutzers
|
||||||
|
*
|
||||||
|
* Ausgabe:
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* Mögliche Fehler:
|
||||||
|
* @throws InvalidArgumentException wenn die E-Mail-Adresse bereits verwendet wird
|
||||||
|
* @throws RuntimeException wenn die Benutzerdaten nicht gelesen oder gespeichert werden können
|
||||||
|
*/
|
||||||
|
public function addUser($email, $vorname, $nachname, $password);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aktualisiert einen bestehenden Benutzer.
|
||||||
|
*
|
||||||
|
* Funktion:
|
||||||
|
* Ändert die E-Mail-Adresse, den Vornamen, den Nachnamen
|
||||||
|
* und optional das Passwort eines bestehenden Benutzers.
|
||||||
|
*
|
||||||
|
* Eingabe:
|
||||||
|
* @param string $oldEmail Alte E-Mail-Adresse
|
||||||
|
* @param string $newEmail Neue E-Mail-Adresse
|
||||||
|
* @param string $vorname Neuer Vorname
|
||||||
|
* @param string $nachname Neuer Nachname
|
||||||
|
* @param string|null $password Neues Passwort oder null
|
||||||
|
*
|
||||||
|
* Ausgabe:
|
||||||
|
* @return bool true, wenn der Benutzer aktualisiert wurde, sonst false
|
||||||
|
*
|
||||||
|
* Mögliche Fehler:
|
||||||
|
* @throws InvalidArgumentException wenn die neue E-Mail-Adresse bereits verwendet wird
|
||||||
|
* @throws RuntimeException wenn die Benutzerdaten nicht gelesen oder gespeichert werden können
|
||||||
|
*/
|
||||||
|
public function updateUser($oldEmail, $newEmail, $vorname, $nachname, $password = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Löscht einen Benutzer anhand seiner E-Mail-Adresse.
|
||||||
|
*
|
||||||
|
* Funktion:
|
||||||
|
* Entfernt einen vorhandenen Benutzer aus der Datenquelle.
|
||||||
|
*
|
||||||
|
* Eingabe:
|
||||||
|
* @param string $email E-Mail-Adresse des zu löschenden Benutzers
|
||||||
|
*
|
||||||
|
* Ausgabe:
|
||||||
|
* @return bool true, wenn der Benutzer gelöscht wurde, sonst false
|
||||||
|
*
|
||||||
|
* Mögliche Fehler:
|
||||||
|
* @throws RuntimeException wenn die Benutzerdaten nicht gelesen oder gespeichert werden können
|
||||||
|
*/
|
||||||
|
public function deleteUser($email);
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft, ob der Titel die folgenden Bedingungen erfüllt:
|
||||||
|
* Buchstaben von a-z; A-Z
|
||||||
|
* Zahlen von 0-9
|
||||||
|
* Umlaute äöüÄÖÜß
|
||||||
|
* Satzeichen .,!?:;()"„“«»_+-
|
||||||
|
* 5-120 Zeichen
|
||||||
|
* @param $title
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
function articleTitleValidator($title)
|
||||||
|
{
|
||||||
|
$title = trim($title);
|
||||||
|
$titlePattern = '/^[a-zA-Z0-9äöüÄÖÜß\s.,!?:;()\'"„“«»_+-]{5,120}$/u';
|
||||||
|
if (preg_match($titlePattern, $title)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft, ob der Content valides JSON ist UND ob alle enthaltenen Blöcke
|
||||||
|
* die inhaltlichen Kriterien erfüllen:
|
||||||
|
* Textblöcke sind nicht leer
|
||||||
|
* Bilder sind in Bildblöcken vorhanden
|
||||||
|
*
|
||||||
|
* @param mixed $content Der zu prüfende JSON-String
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
function articleContentValidator($content)
|
||||||
|
{
|
||||||
|
// 1. Grundlegende Typprüfung
|
||||||
|
if (!is_string($content)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Formale JSON-Prüfung (Kompatibel mit PHP 8.2)
|
||||||
|
$blocks = json_decode($content, true);
|
||||||
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Inhaltliche Validierung der einzelnen Blöcke
|
||||||
|
// Falls das JSON zwar valide, aber kein Array ist (z.B. nur ein String/Zahl)
|
||||||
|
if (!is_array($blocks)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mindestens ein Block sollte vorhanden sein (optional, verhindert leere Beiträge)
|
||||||
|
if (empty($blocks)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($blocks as $block) {
|
||||||
|
// Jeder Block muss die Keys 'type' und 'value' besitzen
|
||||||
|
if (!isset($block['type']) || !isset($block['value'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = $block['type'];
|
||||||
|
$value = $block['value'];
|
||||||
|
|
||||||
|
if ($type === 'text') {
|
||||||
|
// Validierung für Text: Darf nach dem Trimmen nicht leer sein
|
||||||
|
if (trim($value) === '') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} elseif ($type === 'image') {
|
||||||
|
// Validierung für Bild: Muss entweder mit uploads/ starten (Bestand)
|
||||||
|
// oder mit data:image/ beginnen (neues Base64-Bild aus dem Editor)
|
||||||
|
if (!is_string($value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$isValidPath = str_starts_with($value, 'uploads/');
|
||||||
|
$isValidBase64 = str_starts_with($value, 'data:image/');
|
||||||
|
|
||||||
|
if (!$isValidPath && !$isValidBase64) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Unbekannter Blocktyp wird zur Sicherheit abgewiesen
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wenn alle Prüfungen bestanden wurden
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft, ob die Kategorie eine erlaubt Kategorie ist.
|
||||||
|
* @param $category
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
function articleCategoryValidator($category)
|
||||||
|
{
|
||||||
|
$allowedCategories = [
|
||||||
|
'deutsch', 'englisch', 'franzoesisch', 'latein', 'literatur',
|
||||||
|
'mathe', 'biologie', 'chemie', 'physik', 'informatik', 'astronomie',
|
||||||
|
'geschichte', 'erdkunde', 'sozialkunde', 'wirtschaft', 'religion',
|
||||||
|
'ethik', 'philosophie', 'psychologie', 'kunst', 'musik', 'theater',
|
||||||
|
'technik', 'werken', 'hauswirtschaft', 'sport'
|
||||||
|
];
|
||||||
|
if (in_array($category, $allowedCategories, true)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft, ob die Tags die folgenden Bedingungen erfüllen:
|
||||||
|
* Buchstaben von a-z; A-Z
|
||||||
|
* Zahlen von 0-9
|
||||||
|
* Umlaute äöüÄÖÜß
|
||||||
|
* Satzeichen -
|
||||||
|
* 2-50 Zeichen
|
||||||
|
* @param $tags
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
function articleTagValidator($tags)
|
||||||
|
{
|
||||||
|
if (!isset($tags)) {
|
||||||
|
$tags = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$rawTags = explode(',', $tags);
|
||||||
|
|
||||||
|
foreach ($rawTags as $rawTag) {
|
||||||
|
// Leerzeichen am Anfang/Ende des einzelnen Tags entfernen:
|
||||||
|
$tag = trim($rawTag);
|
||||||
|
|
||||||
|
// leere Elemente überspringen:
|
||||||
|
if ($tag === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tag mit Regex prüfen:
|
||||||
|
$tagPattern = '/^[a-zA-Z0-9äöüÄÖÜß\s-]{2,50}$/u';
|
||||||
|
if (!preg_match($tagPattern, $tag)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft, ob eine Suchanfrage gültig ist.
|
||||||
|
*
|
||||||
|
* Erlaubt werden Buchstaben, Zahlen, Umlaute, typische Satzzeichen und Leerzeichen.
|
||||||
|
* Die Länge muss zwischen 1 und 50 Zeichen liegen.
|
||||||
|
*
|
||||||
|
* @param string $query Zu prüfender Suchbegriff
|
||||||
|
*
|
||||||
|
* @return bool true wenn die Suche gültig ist, sonst false
|
||||||
|
*/
|
||||||
|
function searchQueryValidator($query)
|
||||||
|
{
|
||||||
|
$query = trim($query);
|
||||||
|
|
||||||
|
// Mindestens 1 Zeichen, maximal 50 Zeichen
|
||||||
|
$length = mb_strlen($query);
|
||||||
|
if ($length < 1 || $length > 50) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erlaubt Buchstaben (inkl. Umlaut/ß), Zahlen, Leerzeichen sowie ?, !, ., -, _
|
||||||
|
$searchPattern = '/^[a-zA-Z0-9äöüÄÖÜß\s?!.,\-_]+$/u';
|
||||||
|
|
||||||
|
return preg_match($searchPattern, $query) === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft, ob das gewählte Treffer-Limit erlaubt ist.
|
||||||
|
*
|
||||||
|
* @param int|string $limit Das zu prüfende Limit
|
||||||
|
*
|
||||||
|
* @return bool true wenn das Limit 10, 20, 50 oder 100 ist, sonst false
|
||||||
|
*/
|
||||||
|
function searchLimitValidator($limit)
|
||||||
|
{
|
||||||
|
$allowedLimits = [10, 20, 50, 100];
|
||||||
|
return in_array((int)$limit, $allowedLimits, true);
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft, ob ein Vor- oder Nachname gültig ist.
|
||||||
|
*
|
||||||
|
* Erlaubt werden Buchstaben, Umlaute, Leerzeichen und Bindestriche.
|
||||||
|
* Die Länge muss zwischen 2 und 20 Zeichen liegen.
|
||||||
|
*
|
||||||
|
* @param string $name Zu prüfender Name
|
||||||
|
*
|
||||||
|
* @return bool true wenn der Name gültig ist, sonst false
|
||||||
|
*/
|
||||||
|
function userNameValidator($name)
|
||||||
|
{
|
||||||
|
$name = trim($name);
|
||||||
|
$namePattern = '/^[a-zA-ZäöüÄÖÜß\s-]{2,20}$/u';
|
||||||
|
|
||||||
|
return preg_match($namePattern, $name) === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft, ob eine E-Mail-Adresse gültig ist.
|
||||||
|
*
|
||||||
|
* Die E-Mail-Adresse muss dem Standardformat entsprechen
|
||||||
|
* und darf höchstens 100 Zeichen lang sein.
|
||||||
|
*
|
||||||
|
* @param string $email Zu prüfende E-Mail-Adresse
|
||||||
|
*
|
||||||
|
* @return bool true wenn die E-Mail-Adresse gültig ist, sonst false
|
||||||
|
*/
|
||||||
|
function userEmailValidator($email)
|
||||||
|
{
|
||||||
|
$email = trim($email);
|
||||||
|
|
||||||
|
return filter_var($email, FILTER_VALIDATE_EMAIL) !== false
|
||||||
|
&& mb_strlen($email) <= 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft, ob ein Passwort die Längenanforderungen erfüllt.
|
||||||
|
*
|
||||||
|
* Das Passwort muss zwischen 5 und 12 Zeichen lang sein.
|
||||||
|
*
|
||||||
|
* @param string $password Zu prüfendes Passwort
|
||||||
|
*
|
||||||
|
* @return bool true wenn das Passwort gültig ist, sonst false
|
||||||
|
*/
|
||||||
|
function userPasswordValidator($password)
|
||||||
|
{
|
||||||
|
$zeichenAnzahl = mb_strlen($password);
|
||||||
|
|
||||||
|
return $zeichenAnzahl >= 5 && $zeichenAnzahl <= 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft ein optionales Passwort.
|
||||||
|
*
|
||||||
|
* Ein leeres Passwort ist erlaubt und bedeutet,
|
||||||
|
* dass das bestehende Passwort unverändert bleibt.
|
||||||
|
* Falls ein Passwort angegeben wurde, wird es
|
||||||
|
* mit den normalen Passwortregeln geprüft.
|
||||||
|
*
|
||||||
|
* @param string|null $password Zu prüfendes Passwort
|
||||||
|
*
|
||||||
|
* @return bool true wenn das Passwort gültig oder leer ist,
|
||||||
|
* sonst false
|
||||||
|
*/
|
||||||
|
function userOptionalPasswordValidator($password)
|
||||||
|
{
|
||||||
|
if (!isset($password) || $password === '') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return userPasswordValidator($password);
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -1,52 +0,0 @@
|
|||||||
<!--
|
|
||||||
Artikel: Satz des Pythagoras
|
|
||||||
Funktion: Inhalt zum Fach Mathe
|
|
||||||
-->
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="de">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="description" content="EduForge">
|
|
||||||
<meta name="author" content="Niklas Ortmann">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<link rel="icon" type="image/x-icon" href="images/logos/logo_icon.ico">
|
|
||||||
<link rel="stylesheet" href="css/main.css">
|
|
||||||
<link rel="stylesheet" href="css/navbar.css">
|
|
||||||
<link rel="stylesheet" href="css/footer.css">
|
|
||||||
<title>EduForge</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
include_once 'includes/navbar.php';
|
|
||||||
?>
|
|
||||||
<main>
|
|
||||||
|
|
||||||
<h1>Satz des Pythagoras</h1>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Der Satz des Pythagoras wurde von dem griechischen Philosophen Pythagoras von Samos formuliert und im
|
|
||||||
dritten Jahrhundert vor Christus veröffentlicht.
|
|
||||||
</p>
|
|
||||||
<figure>
|
|
||||||
<img src="https://cdn8.picryl.com/photo/2016/05/14/pythagoras-e9560b-1024.jpg" alt="Trulli" style="width:10%">
|
|
||||||
<figcaption><a href="https://cdn8.picryl.com/photo/2016/05/14/pythagoras-e9560b-1024.jpg">Quelle</a></figcaption>
|
|
||||||
</figure>
|
|
||||||
<p>
|
|
||||||
In der beigefügten Abbildung sehen wir ein rechtwinkliges Dreieck, dessen drei Seiten die Längen a, b und c
|
|
||||||
besitzen. Auf jeder Seite ist ein Quadrat konstruiert. Das Quadrat auf der Seite a hat die Fläche a2, das
|
|
||||||
Quadrat auf der Seite b hat die Fläche b2 und das Quadrat auf der Seite c hat die Fläche c2. Der Satz des
|
|
||||||
Pythagoras besagt, dass die Summe der Flächen der beiden kleineren Quadrate gleich der Fläche des größten
|
|
||||||
Quadrats ist. Das bedeutet, dass a^2+b^2=c^2 (Satz des Pythagoras).
|
|
||||||
</p>
|
|
||||||
<a href="docs/Uebungsaufgaben_Pythagoras.pdf">Lernzettel-Download</a>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
include_once 'includes/footer.php';
|
|
||||||
?>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
<!--
|
|
||||||
Artikel: Tunneleffekt
|
|
||||||
Funktion: Inhalt zum Fach Physik
|
|
||||||
-->
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="de">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="description" content="EduForge">
|
|
||||||
<meta name="author" content="Niklas Ortmann">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<link rel="icon" type="image/x-icon" href="images/logos/logo_icon.ico">
|
|
||||||
<link rel="stylesheet" href="css/main.css">
|
|
||||||
<link rel="stylesheet" href="css/navbar.css">
|
|
||||||
<link rel="stylesheet" href="css/footer.css">
|
|
||||||
<title>EduForge</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
include_once 'includes/navbar.php';
|
|
||||||
?>
|
|
||||||
<main>
|
|
||||||
|
|
||||||
<h1>Tunneleffekt</h1>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Der Tunneleffekt ist ein quantenmechanisches Phänomen, bei dem Teilchen – vor allem Elektronen – eine
|
|
||||||
Energiebarriere überwinden können, obwohl sie nach den Regeln der klassischen Physik nicht genügend Energie
|
|
||||||
dafür besitzen. In der klassischen Vorstellung müsste ein Elektron entweder genügend Energie haben, um über
|
|
||||||
eine Barriere zu „springen“, oder es würde vollständig zurückgeworfen. In der Quantenphysik wird ein Elektron
|
|
||||||
jedoch nicht als punktförmiges Teilchen beschrieben, sondern als Wellenfunktion, die sich räumlich ausdehnt.
|
|
||||||
Dadurch besteht eine endliche Wahrscheinlichkeit, dass sich das Elektron auf der anderen Seite einer Barriere
|
|
||||||
befindet (Griffiths & Schroeter, 2018).
|
|
||||||
</p>
|
|
||||||
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
include_once 'includes/footer.php';
|
|
||||||
?>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Verhindert das Auflisten aller Dateien im Ordner
|
||||||
|
Options -Indexes
|
||||||
|
|
||||||
|
# Schaltet die PHP-Ausführung in diesem Ordner komplett ab
|
||||||
|
<FilesMatch "\.(php|php[0-9]|phtml|pl|py|jsp|sh|cgi)$">
|
||||||
|
Order Deny,Allow
|
||||||
|
Deny from all
|
||||||
|
</FilesMatch>
|
||||||
|
|
||||||
|
# erlaubt nur den Zugriff auf folgende Dateien:
|
||||||
|
<FilesMatch "\.(?i:jpg|jpeg|png|gif|webp|ico)$">
|
||||||
|
Order Allow,Deny
|
||||||
|
Allow from all
|
||||||
|
</FilesMatch>
|
||||||
Reference in New Issue
Block a user