editor-projects #29
							
								
								
									
										3
									
								
								.fleet/settings.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.fleet/settings.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
{
 | 
			
		||||
    "editor.guides": []
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										16
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							@ -395,16 +395,16 @@
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "laminas/laminas-httphandlerrunner",
 | 
			
		||||
            "version": "2.4.0",
 | 
			
		||||
            "version": "2.5.0",
 | 
			
		||||
            "source": {
 | 
			
		||||
                "type": "git",
 | 
			
		||||
                "url": "https://github.com/laminas/laminas-httphandlerrunner.git",
 | 
			
		||||
                "reference": "d15af53895fd581b5a448a09fd9a4baebc4ae6e5"
 | 
			
		||||
                "reference": "7a47834aaad7852816d2ec4fdbb0492163b039ae"
 | 
			
		||||
            },
 | 
			
		||||
            "dist": {
 | 
			
		||||
                "type": "zip",
 | 
			
		||||
                "url": "https://api.github.com/repos/laminas/laminas-httphandlerrunner/zipball/d15af53895fd581b5a448a09fd9a4baebc4ae6e5",
 | 
			
		||||
                "reference": "d15af53895fd581b5a448a09fd9a4baebc4ae6e5",
 | 
			
		||||
                "url": "https://api.github.com/repos/laminas/laminas-httphandlerrunner/zipball/7a47834aaad7852816d2ec4fdbb0492163b039ae",
 | 
			
		||||
                "reference": "7a47834aaad7852816d2ec4fdbb0492163b039ae",
 | 
			
		||||
                "shasum": ""
 | 
			
		||||
            },
 | 
			
		||||
            "require": {
 | 
			
		||||
@ -416,9 +416,9 @@
 | 
			
		||||
            "require-dev": {
 | 
			
		||||
                "laminas/laminas-coding-standard": "~2.4.0",
 | 
			
		||||
                "laminas/laminas-diactoros": "^2.18",
 | 
			
		||||
                "phpunit/phpunit": "^9.5.25",
 | 
			
		||||
                "psalm/plugin-phpunit": "^0.17.0",
 | 
			
		||||
                "vimeo/psalm": "^4.28"
 | 
			
		||||
                "phpunit/phpunit": "^9.5.26",
 | 
			
		||||
                "psalm/plugin-phpunit": "^0.18.0",
 | 
			
		||||
                "vimeo/psalm": "^5.0.0"
 | 
			
		||||
            },
 | 
			
		||||
            "type": "library",
 | 
			
		||||
            "extra": {
 | 
			
		||||
@ -458,7 +458,7 @@
 | 
			
		||||
                    "type": "community_bridge"
 | 
			
		||||
                }
 | 
			
		||||
            ],
 | 
			
		||||
            "time": "2022-10-25T13:41:39+00:00"
 | 
			
		||||
            "time": "2023-01-05T21:54:03+00:00"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "laravel/serializable-closure",
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										4
									
								
								dist/api/.htaccess
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								dist/api/.htaccess
									
									
									
									
										vendored
									
									
								
							@ -1,4 +0,0 @@
 | 
			
		||||
RewriteEngine On
 | 
			
		||||
RewriteCond %{REQUEST_FILENAME} !-f
 | 
			
		||||
RewriteCond %{REQUEST_FILENAME} !-d
 | 
			
		||||
RewriteRule ^ index.php [QSA,L]
 | 
			
		||||
							
								
								
									
										127
									
								
								dist/api/index.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										127
									
								
								dist/api/index.php
									
									
									
									
										vendored
									
									
								
							@ -1,6 +1,5 @@
 | 
			
		||||
<?php /** @noinspection PhpIncludeInspection */
 | 
			
		||||
 | 
			
		||||
session_start();
 | 
			
		||||
////////////////// Index file //////////////
 | 
			
		||||
/// Creates base routes and runs         ///
 | 
			
		||||
/// respective functions                 ///
 | 
			
		||||
@ -54,6 +53,8 @@ $app->get("/timelineData/{timeline}", function (Request $request, Response $resp
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    // something went wrong
 | 
			
		||||
    $response->getBody()->write(json_encode(array("errorMessage" => "Error, timeline data not found")));
 | 
			
		||||
    return $response->withStatus(404);
 | 
			
		||||
@ -80,10 +81,10 @@ $app->patch("/timelineData/{timeline}/{id}", function (Request $request, Respons
 | 
			
		||||
            return $response->withStatus(500);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $response->withStatus(200);
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ($args["timeline"] == "work" && $args["id"] != null)
 | 
			
		||||
    if ($args["timeline"] == "work" && $args["id"] != "undefined")
 | 
			
		||||
    {
 | 
			
		||||
        if (empty($data["dateFrom"]) || empty($data["dateTo"]) || empty($data["companyName"]) || empty($data["area"]) || empty($data["title"]))
 | 
			
		||||
        {
 | 
			
		||||
@ -99,7 +100,7 @@ $app->patch("/timelineData/{timeline}/{id}", function (Request $request, Respons
 | 
			
		||||
            return $response->withStatus(500);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $response->withStatus(200);
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $response->getBody()->write(json_encode(array("error" => "The correct data was not sent")));
 | 
			
		||||
@ -118,7 +119,7 @@ $app->delete("/timelineData/{timeline}/{id}", function (Request $request, Respon
 | 
			
		||||
            return $response->withStatus(500);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $response->withStatus(200);
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ($args["timeline"] == "work" && $args["id"] != null)
 | 
			
		||||
@ -130,7 +131,7 @@ $app->delete("/timelineData/{timeline}/{id}", function (Request $request, Respon
 | 
			
		||||
            return $response->withStatus(500);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $response->withStatus(200);
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $response->getBody()->write(json_encode(array("error" => "The correct data was not sent")));
 | 
			
		||||
@ -159,7 +160,7 @@ $app->post("/timelineData/{timeline}", function (Request $request, Response $res
 | 
			
		||||
        }
 | 
			
		||||
    
 | 
			
		||||
        $response->getBody()->write(json_encode(array("ID" => $insertedID)));
 | 
			
		||||
        return $response->withStatus(200);
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ($args["timeline"] == "work")
 | 
			
		||||
@ -185,7 +186,7 @@ $app->post("/timelineData/{timeline}", function (Request $request, Response $res
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        $response->getBody()->write(json_encode(array("ID" => $insertedID)));
 | 
			
		||||
        return $response->withStatus(200);
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $response->getBody()->write(json_encode(array("error" => "The correct data was not sent")));
 | 
			
		||||
@ -211,6 +212,105 @@ $app->get("/projectData", function (Request $request, Response $response)
 | 
			
		||||
    return $response;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$app->patch("/projectData/{id}", function (Request $request, Response $response, array $args)
 | 
			
		||||
{
 | 
			
		||||
    global $projectData;
 | 
			
		||||
    $data = $request->getParsedBody();
 | 
			
		||||
    if ($args["id"] != "undefined")
 | 
			
		||||
    {
 | 
			
		||||
        if (empty($data["title"]) || empty($data["isMainProject"]) || empty($data["information"]) || empty($data["gitLink"]))
 | 
			
		||||
        {
 | 
			
		||||
            // uh oh sent some empty data
 | 
			
		||||
            $response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
 | 
			
		||||
            return $response->withStatus(400);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!$projectData->updateProjectData($args["id"], $data["title"], $data["isMainProject"], $data["information"], $data["projectLink"], $data["gitLink"]))
 | 
			
		||||
        {
 | 
			
		||||
            // uh oh something went wrong
 | 
			
		||||
            $response->getBody()->write(json_encode(array("error" => "Something went wrong", "data" => $projectData->updateProjectData($args["id"], $data["title"], $data["isMainProject"], $data["information"], $data["projectLink"], $data["gitLink"]))));
 | 
			
		||||
            return $response->withStatus(500);
 | 
			
		||||
        }
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $response->getBody()->write(json_encode(array("error" => "Please provide an ID")));
 | 
			
		||||
    return $response->withStatus(400);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$app->delete("/projectData/{id}", function (Request $request, Response $response, array $args)
 | 
			
		||||
{
 | 
			
		||||
    global $projectData;
 | 
			
		||||
    if ($args["id"] != null)
 | 
			
		||||
    {
 | 
			
		||||
        $message = $projectData->deleteProjectData($args["id"]);
 | 
			
		||||
        if ($message === "error")
 | 
			
		||||
        {
 | 
			
		||||
            // uh oh something went wrong
 | 
			
		||||
            $response->getBody()->write(json_encode(array("error" => "Something went wrong or the project with ID ".$args["id"]."does not exist")));
 | 
			
		||||
            return $response->withStatus(500);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($message === "cannot delete")
 | 
			
		||||
        {
 | 
			
		||||
            //uh oh cannot delete the main project
 | 
			
		||||
            $response->getBody()->write(json_encode(array("error" => "Cannot delete the main project")));
 | 
			
		||||
            return $response->withStatus(409);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $response->getBody()->write(json_encode(array("error" => "Please provide an ID")));
 | 
			
		||||
    return $response->withStatus(400);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$app->post("/projectData", function (Request $request, Response $response)
 | 
			
		||||
{
 | 
			
		||||
    global $projectData;
 | 
			
		||||
    $data = $request->getParsedBody();
 | 
			
		||||
    if (empty($data["title"]) || empty($data["isMainProject"]) || empty($data["information"]) || empty($data["gitLink"]))
 | 
			
		||||
    {
 | 
			
		||||
        // uh oh sent some empty data
 | 
			
		||||
        $response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
 | 
			
		||||
        return $response->withStatus(400);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $insertedID = $projectData->addProjectData($data["title"], $data["isMainProject"], $data["information"], $data["projectLink"], $data["gitLink"]);
 | 
			
		||||
    if (!is_int($insertedID))
 | 
			
		||||
    {
 | 
			
		||||
        // uh oh something went wrong
 | 
			
		||||
        $response->getBody()->write(json_encode(array("error" => "Something went wrong", "message" => $insertedID)));
 | 
			
		||||
        return $response->withStatus(500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $response->getBody()->write(json_encode(array("ID" => $insertedID)));
 | 
			
		||||
    return $response;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$app->post("/projectImage/{id}", function (Request $request, Response $response, array $args)
 | 
			
		||||
{
 | 
			
		||||
    global $projectData;
 | 
			
		||||
    $files = $request->getUploadedFiles();
 | 
			
		||||
    if (empty($args["id"]) || empty($files))
 | 
			
		||||
    {
 | 
			
		||||
        // uh oh only some of the data was sent
 | 
			
		||||
        $response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
 | 
			
		||||
        return $response->withStatus(400);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $message = $projectData->uploadImage($args["id"], $files["img"]);
 | 
			
		||||
    if (!is_array($message))
 | 
			
		||||
    {
 | 
			
		||||
        // uh oh something went wrong
 | 
			
		||||
        $response->getBody()->write(json_encode(array("error" => $message)));
 | 
			
		||||
        return $response->withStatus(500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $response->getBody()->write(json_encode($message));
 | 
			
		||||
    return $response;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$app->post("/contact", function (Request $request, Response $response)
 | 
			
		||||
{
 | 
			
		||||
    $data = $request->getParsedBody();
 | 
			
		||||
@ -218,6 +318,7 @@ $app->post("/contact", function (Request $request, Response $response)
 | 
			
		||||
    {
 | 
			
		||||
      $response->getBody()->write(json_encode(array("errorMessage" => "Please fill out all the fields")));
 | 
			
		||||
      return $response->withStatus(400);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    if (!filter_var($data["email"], FILTER_VALIDATE_EMAIL)) 
 | 
			
		||||
@ -409,6 +510,12 @@ $app->post("/user/login", function (Request $request, Response $response)
 | 
			
		||||
    return $response->withStatus(401);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$app->get("/user/logout", function (Request $request, Response $response)
 | 
			
		||||
{
 | 
			
		||||
    session_unset();
 | 
			
		||||
    return $response;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$app->get("/user/isLoggedIn", function (Request $request, Response $response) 
 | 
			
		||||
{
 | 
			
		||||
    global $user;
 | 
			
		||||
@ -507,8 +614,4 @@ $app->post("/user/changePassword", function (Request $request, Response $respons
 | 
			
		||||
    return $response->withStatus(500);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
$app->run();
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										41
									
								
								dist/api/middleware.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										41
									
								
								dist/api/middleware.php
									
									
									
									
										vendored
									
									
								
							@ -2,9 +2,17 @@
 | 
			
		||||
// middleware
 | 
			
		||||
namespace api;
 | 
			
		||||
 | 
			
		||||
session_start();
 | 
			
		||||
 | 
			
		||||
use Psr\Http\Message\ServerRequestInterface;
 | 
			
		||||
use Psr\Http\Server\RequestHandlerInterface;
 | 
			
		||||
use Slim\App;
 | 
			
		||||
use Selective\SameSiteCookie\SameSiteCookieConfiguration;
 | 
			
		||||
use Selective\SameSiteCookie\SameSiteCookieMiddleware;
 | 
			
		||||
use Slim\Exception\HttpInternalServerErrorException;
 | 
			
		||||
use Slim\Exception\HttpMethodNotAllowedException;
 | 
			
		||||
use Slim\Exception\HttpNotFoundException;
 | 
			
		||||
use Slim\Psr7\Response;
 | 
			
		||||
use Tuupola\Middleware\JwtAuthentication;
 | 
			
		||||
use Tuupola\Middleware\JwtAuthentication\RequestMethodRule;
 | 
			
		||||
use Tuupola\Middleware\JwtAuthentication\RequestPathRule;
 | 
			
		||||
@ -24,6 +32,7 @@ class middleware
 | 
			
		||||
        $this->baseMiddleware($app);
 | 
			
		||||
        $this->sameSiteConfig($app);
 | 
			
		||||
        $this->jwtAuth($app);
 | 
			
		||||
        $this->errorHandling($app);
 | 
			
		||||
        $this->returnAsJSON($app);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -70,7 +79,7 @@ class middleware
 | 
			
		||||
        $app->add(new JwtAuthentication([
 | 
			
		||||
            "rules" => [
 | 
			
		||||
                new RequestPathRule([
 | 
			
		||||
                    "path" => ["/api/projectData", "/api/timeline/[a-z]*", "/api/user/testMethod"],
 | 
			
		||||
                    "path" => ["/api/projectData", "/api/timeline/[a-z]*", "/api/logout"],
 | 
			
		||||
                    "ignore" => ["/api/contact", "/api/user/login", "/api/user/changePassword"]
 | 
			
		||||
                ]),
 | 
			
		||||
                new RequestMethodRule([
 | 
			
		||||
@ -86,7 +95,37 @@ class middleware
 | 
			
		||||
                return $response->withStatus(401);
 | 
			
		||||
            }
 | 
			
		||||
        ]));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function errorHandling(App $app): void
 | 
			
		||||
    {
 | 
			
		||||
        $app->add(function (ServerRequestInterface $request, RequestHandlerInterface $handler)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                return $handler->handle($request);
 | 
			
		||||
            }
 | 
			
		||||
            catch (HttpNotFoundException $httpException)
 | 
			
		||||
            {
 | 
			
		||||
                $response = (new Response())->withStatus(404);
 | 
			
		||||
                $response->getBody()->write(json_encode(array("status" => "404", "message" => $request->getUri()->getPath() . " not found")));
 | 
			
		||||
                return $response;
 | 
			
		||||
            }
 | 
			
		||||
            catch (HttpMethodNotAllowedException $httpException)
 | 
			
		||||
            {
 | 
			
		||||
                $response = (new Response())->withStatus(405);
 | 
			
		||||
                $response->getBody()->write(json_encode(array("status" => "405", "message" => "Method not allowed")));
 | 
			
		||||
                return $response;
 | 
			
		||||
            }
 | 
			
		||||
            catch (HttpInternalServerErrorException $exception)
 | 
			
		||||
            {
 | 
			
		||||
                $response = (new Response())->withStatus(500);
 | 
			
		||||
                $response->getBody()->write(json_encode(array("status" => "500", "message" => $exception->getMessage())));
 | 
			
		||||
                return $response;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        $app->addErrorMiddleware(true, true, true);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										179
									
								
								dist/api/projectData.php
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										179
									
								
								dist/api/projectData.php
									
									
									
									
										vendored
									
									
								
							@ -1,6 +1,7 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace api;
 | 
			
		||||
use PDO;
 | 
			
		||||
use Psr\Http\Message\UploadedFileInterface;
 | 
			
		||||
 | 
			
		||||
require_once "./config.php";
 | 
			
		||||
 | 
			
		||||
@ -17,7 +18,7 @@ class projectData
 | 
			
		||||
    function getProjectData(): array
 | 
			
		||||
    {
 | 
			
		||||
        $conn = dbConn();
 | 
			
		||||
        $stmt = $conn->prepare("SELECT title, isMainProject, information, imgLocation, projectLink, githubLink FROM projects order by date LIMIT 4;");
 | 
			
		||||
        $stmt = $conn->prepare("SELECT ID, title, isMainProject, information, imgLocation, projectLink, gitLink FROM projects ORDER BY isMainProject DESC;");
 | 
			
		||||
        $stmt->execute();
 | 
			
		||||
 | 
			
		||||
        // set the resulting array to associative
 | 
			
		||||
@ -27,6 +28,182 @@ class projectData
 | 
			
		||||
        {
 | 
			
		||||
            return $result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return array("errorMessage" => "Error, project data not found");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update project data in the database with the given ID
 | 
			
		||||
     * @param string $ID - ID of the project in the database to update
 | 
			
		||||
     * @param string $title - Title of the project
 | 
			
		||||
     * @param string $isMainProject - Is the project a main project or not
 | 
			
		||||
     * @param string $information - Information about the project
 | 
			
		||||
     * @param string $projectLink - Link to the project
 | 
			
		||||
     * @param string $gitLink - Link to the git repository
 | 
			
		||||
     * @return bool - True if project was updated, false if not and there was an error
 | 
			
		||||
     */
 | 
			
		||||
    function updateProjectData(string $ID, string $title, string $isMainProject, string $information, string $projectLink, string $gitLink): bool
 | 
			
		||||
    {
 | 
			
		||||
        $conn = dbConn();
 | 
			
		||||
 | 
			
		||||
        if ($isMainProject === "true")
 | 
			
		||||
        {
 | 
			
		||||
            $stmtMainProject = $conn->prepare("UPDATE projects SET isMainProject = 0;");
 | 
			
		||||
            $stmtMainProject->execute();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $stmt = $conn->prepare("UPDATE projects SET title = :title, isMainProject = :isMainProject, information = :information,  projectLink = :projectLink, gitLink = :gitLink WHERE ID = :ID");
 | 
			
		||||
        $stmt->bindParam(":title", $title);
 | 
			
		||||
        $isMainProj = ($isMainProject) ? 1 : 0;
 | 
			
		||||
        $stmt->bindParam(":isMainProject", $isMainProj);
 | 
			
		||||
        $stmt->bindParam(":information", $information);
 | 
			
		||||
        $stmt->bindParam(":projectLink", $projectLink);
 | 
			
		||||
        $stmt->bindParam(":gitLink", $gitLink);
 | 
			
		||||
        $stmt->bindParam(":ID", $ID);
 | 
			
		||||
        return $stmt->execute();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete project data from the database
 | 
			
		||||
     * @param int $ID - ID of the project in the database to delete
 | 
			
		||||
     * @return string - True if project was deleted, false if not and there was an error
 | 
			
		||||
     */
 | 
			
		||||
    function deleteProjectData(int $ID): string
 | 
			
		||||
    {
 | 
			
		||||
        $conn = dbConn();
 | 
			
		||||
 | 
			
		||||
        // check if the project is a main project if it is return false
 | 
			
		||||
 | 
			
		||||
        $stmtMainProject = $conn->prepare("SELECT isMainProject FROM projects WHERE ID = :ID");
 | 
			
		||||
        $stmtMainProject->bindParam(":ID", $ID);
 | 
			
		||||
        $stmtMainProject->execute();
 | 
			
		||||
        $result = $stmtMainProject->fetch(PDO::FETCH_ASSOC);
 | 
			
		||||
 | 
			
		||||
        if ($result["isMainProject"] === "1")
 | 
			
		||||
        {
 | 
			
		||||
            return "cannot delete";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->deleteImage($ID);
 | 
			
		||||
 | 
			
		||||
        $stmt = $conn->prepare("DELETE FROM projects WHERE ID = :ID");
 | 
			
		||||
        $stmt->bindParam(":ID", $ID);
 | 
			
		||||
        $stmt->execute();
 | 
			
		||||
 | 
			
		||||
        if ($stmt->rowCount() > 0)
 | 
			
		||||
        {
 | 
			
		||||
            return "ok";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return "error";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add project data to the database
 | 
			
		||||
     * @param string $title - Title of the project
 | 
			
		||||
     * @param string $isMainProject - Is the project a main project or not
 | 
			
		||||
     * @param string $information - Information about the project
 | 
			
		||||
     * @param string $projectLink - Link to the project
 | 
			
		||||
     * @param string $gitLink - Link to the github repository
 | 
			
		||||
     * @return int|bool - ID of the project if it was added, false if not and there was an error
 | 
			
		||||
     */
 | 
			
		||||
    function addProjectData(string $title, string $isMainProject, string $information, string $projectLink, string $gitLink): int|bool
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $conn = dbConn();
 | 
			
		||||
        if ($isMainProject === "true")
 | 
			
		||||
        {
 | 
			
		||||
            $stmtMainProject = $conn->prepare("UPDATE projects SET isMainProject = 0;");
 | 
			
		||||
            $stmtMainProject->execute();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $stmt = $conn->prepare("INSERT INTO projects (title, isMainProject, information, projectLink, gitLink) VALUES (:title, :isMainProject, :information, :projectLink, :gitLink)");
 | 
			
		||||
        $stmt->bindParam(":title", $title);
 | 
			
		||||
        $isMainProj = ($isMainProject) ? 1 : 0;
 | 
			
		||||
        $stmt->bindParam(":isMainProject", $isMainProj);
 | 
			
		||||
        $stmt->bindParam(":information", $information);
 | 
			
		||||
        $stmt->bindParam(":projectLink", $projectLink);
 | 
			
		||||
        $stmt->bindParam(":gitLink", $gitLink);
 | 
			
		||||
        $stmt->execute();
 | 
			
		||||
 | 
			
		||||
        if ($stmt->rowCount() > 0)
 | 
			
		||||
        {
 | 
			
		||||
            return $conn->lastInsertId();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Upload the image to the server and update the database with the new image location
 | 
			
		||||
     * @param int $ID - ID of the project in the database to update
 | 
			
		||||
     * @param UploadedFileInterface $img - Image preview of the project
 | 
			
		||||
     * @return string|array - String with error message or array with the new image location
 | 
			
		||||
     */
 | 
			
		||||
    public function uploadImage(int $ID, UploadedFileInterface $img): string | array
 | 
			
		||||
    {
 | 
			
		||||
        $targetDir = "../imgs/projects/";
 | 
			
		||||
        $targetFile = $targetDir . basename($img->getClientFilename());
 | 
			
		||||
        $uploadOk = 1;
 | 
			
		||||
        $imageFileType = strtolower(pathinfo($targetFile, PATHINFO_EXTENSION));
 | 
			
		||||
 | 
			
		||||
        // Check if file already exists
 | 
			
		||||
        if (file_exists($targetFile))
 | 
			
		||||
        {
 | 
			
		||||
            return "The file already exists";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check file size
 | 
			
		||||
        if ($img->getSize() > 2000000)
 | 
			
		||||
        {
 | 
			
		||||
            return "The file is too large, max 2MB";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Allow certain file formats
 | 
			
		||||
        if ($imageFileType != "jpg" && $imageFileType != "png" && $imageFileType != "jpeg" && $imageFileType != "gif")
 | 
			
		||||
        {
 | 
			
		||||
            return "Only JPG, JPEG, PNG & GIF files are allowed";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $img->moveTo($targetFile);
 | 
			
		||||
 | 
			
		||||
        if (file_exists($targetFile))
 | 
			
		||||
        {
 | 
			
		||||
            $this->deleteImage($ID);
 | 
			
		||||
            // update the database with the new image location
 | 
			
		||||
            $conn = dbConn();
 | 
			
		||||
            $stmt = $conn->prepare("UPDATE projects SET imgLocation = :imgLocation WHERE ID = :ID");
 | 
			
		||||
            $stmt->bindParam(":imgLocation", $targetFile);
 | 
			
		||||
            $stmt->bindParam(":ID", $ID);
 | 
			
		||||
            $stmt->execute();
 | 
			
		||||
 | 
			
		||||
            if ($stmt->rowCount() > 0)
 | 
			
		||||
            {
 | 
			
		||||
                return array("imgLocation" => $targetFile);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return "Couldn't update the database";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return "Couldn't upload the image";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete the image from the server
 | 
			
		||||
     * @param int $ID - ID of the project in the database
 | 
			
		||||
     */
 | 
			
		||||
    private function deleteImage(int $ID): void
 | 
			
		||||
    {
 | 
			
		||||
        $conn = dbConn();
 | 
			
		||||
        $imgStmt = $conn->prepare("SELECT imgLocation FROM projects WHERE ID = :ID");
 | 
			
		||||
        $imgStmt->bindParam(":ID", $ID);
 | 
			
		||||
        $imgStmt->execute();
 | 
			
		||||
        $imgLocation = $imgStmt->fetch(PDO::FETCH_ASSOC)["imgLocation"];
 | 
			
		||||
 | 
			
		||||
        if ($imgLocation != null)
 | 
			
		||||
        {
 | 
			
		||||
            unlink($imgLocation);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								dist/css/main.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								dist/css/main.css
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								dist/editor/css/main.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								dist/editor/css/main.css
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								dist/editor/editor.html
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								dist/editor/editor.html
									
									
									
									
										vendored
									
									
								
							@ -1 +1 @@
 | 
			
		||||
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Editor</title><link rel="stylesheet" href="css/main.css"><script src="https://kit.fontawesome.com/ed3c25598e.js" crossorigin="anonymous"></script></head><body><nav class="sideNav"><a href="#" class="closeBtn" id="navClose">×</a><ul><li><a href="#" class="active"><span><</span>CV<span>></span></a></li><li><a href="#"><span><</span>Projects<span>></span></a></li><li><a href="#"><span><</span>Settings<span>></span></a></li></ul></nav><main class="editor" style="margin-left: 250px;"><div class="title"><span id="navOpen">☰</span><h1>Editor</h1></div><section id="curriculumVitae"><h2>curriculum vitae</h2><div class="cvGrid"><div><h3>Education</h3><div class="editorContainer"><form action="" method="POST" id="addEdu"><div class="formControl"><label for="dateFromE">Date From</label> <input type="date" id="dateFromE" name="dateFromE"></div><div class="formControl"><label for="dateToE">Date To</label> <input type="date" id="dateToE" name="dateToE"></div><div class="formControl"><label for="grade">Grade</label> <input type="text" id="grade" name="grade"></div><div class="formControl"><label for="courseTitle">Course Title</label> <input type="text" id="courseTitle" name="courseTitle"></div><div class="error hidden" id="eduError"><button class="close" type="button">×</button><div></div></div><input type="submit" class="btn btnPrimary boxShadowIn boxShadowOut" value="Add new course"></form><div class="timeline" id="edu"></div></div></div><div><h3>Work</h3><div class="editorContainer"><form action="" method="POST" id="addWork"><div class="formControl"><label for="dateFromW">Date From</label> <input type="date" id="dateFromW" name="dateFromW"></div><div class="formControl"><label for="dateToW">Date To</label> <input type="date" id="dateToW" name="dateToW"></div><div class="formControl"><label for="company">Company</label> <input type="text" id="company" name="company"></div><div class="formControl"><label for="area">Area</label> <input type="text" id="area" name="area"></div><div class="formControl"><label for="jobTitle">Job Title</label> <input type="text" id="jobTitle" name="jobTitle"></div><div class="error hidden" id="workError"><button class="close" type="button">×</button><div></div></div><input type="submit" class="btn btnPrimary boxShadowIn boxShadowOut" value="Add new job"></form><div class="timeline" id="work"></div></div></div></div></section></main><script src="js/editor.js"></script></body></html>
 | 
			
		||||
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Editor</title><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="stylesheet" href="css/main.css"><script src="https://kit.fontawesome.com/ed3c25598e.js" crossorigin="anonymous"></script></head><body><nav class="sideNav"><a href="#" class="closeBtn" id="navClose">×</a><ul><li><a href="#" id="goToCV"><span><</span>CV<span>></span></a></li><li><a href="#" id="goToProjects" class="active"><span><</span>Projects<span>></span></a></li><li><a href="#" id="logout"><span><</span>Logout<span>></span></a></li></ul></nav><main class="editor" style="margin-left: 250px;"><div class="title"><span id="navOpen">☰</span><h1>Editor</h1></div><section id="curriculumVitae"><h2>curriculum vitae</h2><div class="cvGrid"><div><h3>Education</h3><div class="editorContainer"><form action="" method="POST" id="addEdu"><div class="formControl"><label for="dateFromE">Date From</label> <input type="date" id="dateFromE" name="dateFromE" required></div><div class="formControl"><label for="dateToE">Date To</label> <input type="date" id="dateToE" name="dateToE" required></div><div class="formControl"><label for="grade">Grade</label> <input type="text" id="grade" name="grade" required></div><div class="formControl"><label for="courseTitle">Course Title</label> <input type="text" id="courseTitle" name="courseTitle" required></div><div class="error hidden" id="eduError"><button class="close" type="button">×</button><div></div></div><input type="submit" class="btn btnPrimary boxShadowIn boxShadowOut" value="Add new course"></form><div class="timeline" id="edu"></div></div></div><div><h3>Work</h3><div class="editorContainer"><form action="" method="POST" id="addWork"><div class="formControl"><label for="dateFromW">Date From</label> <input type="date" id="dateFromW" name="dateFromW" required></div><div class="formControl"><label for="dateToW">Date To</label> <input type="date" id="dateToW" name="dateToW"></div><div class="formControl"><label for="company">Company</label> <input type="text" id="company" name="company" required></div><div class="formControl"><label for="area">Area</label> <input type="text" id="area" name="area" required></div><div class="formControl"><label for="jobTitle">Job Title</label> <input type="text" id="jobTitle" name="jobTitle" required></div><div class="error hidden" id="workError"><button class="close" type="button">×</button><div></div></div><input type="submit" class="btn btnPrimary boxShadowIn boxShadowOut" value="Add new job"></form><div class="timeline" id="work"></div></div></div></div></section><section id="projects"><h2>projects</h2><div class="projectsGrid"><form action="" id="addProj"><div class="formControl"><label for="projTitle">Title</label> <input type="text" name="projTitle" id="projTitle" required></div><div class="formControl"><label class="checkContainer" for="isMainProject">Is It The Main Project <input type="checkbox" id="isMainProject" name="isMainProject"> <span class="checkmark"></span></label></div><div class="formControl"><label for="projImg">Image</label> <input type="file" name="projImg" id="projImg"></div><div class="formControl"><label for="projInfo">Description</label> <textarea name="projInfo" id="projInfo" required></textarea></div><div class="formControl"><label for="projLink">Project Link</label> <input type="text" name="projLink" id="projLink" pattern="https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)"></div><div class="formControl"><label for="gitLink">Git Link</label> <input type="text" name="gitLink" id="gitLink" pattern="https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)" required></div><div class="error hidden" id="projError"><button class="close" type="button">×</button><div></div></div><input type="submit" value="Add new Project" class="btn btnPrimary boxShadowIn boxShadowOut"></form><div id="projList"></div></div></section></main><script src="js/editor.js"></script></body></html>
 | 
			
		||||
							
								
								
									
										2
									
								
								dist/editor/index.html
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								dist/editor/index.html
									
									
									
									
										vendored
									
									
								
							@ -1 +1 @@
 | 
			
		||||
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Editor</title><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="stylesheet" href="css/main.css"><script src="https://kit.fontawesome.com/ed3c25598e.js" crossorigin="anonymous"></script></head><body><main class="login"><div id="login" class="container shown"><h1>Login To Editor</h1><form action="" method="POST"><div class="formControl"><label for="username">Username</label> <input type="text" id="username" name="username" required></div><div class="formControl"><label for="password">Password</label> <input type="password" id="password" name="password" required> <i class="fa-solid fa-eye"></i></div><div class="error hidden" id="loginError"><button class="close" type="button">×</button><div></div></div><div class="btnContainer"><input type="submit" value="Submit" class="btn btnPrimary boxShadowIn boxShadowOut"> <a href="#" id="resetPwd">Reset Password</a></div></form></div><div id="resetPassword" class="container" style="display: none; transform: translateX(150vw)"><h1>Reset Password</h1><form action="#" method="POST"><div class="formControl"><label for="email">Email</label> <input type="email" id="email" name="email"></div><div class="error hidden" id="resetError"><button class="close" type="button">×</button><div></div></div><div class="btnContainer"><input type="submit" value="Submit" class="btn btnPrimary boxShadowIn boxShadowOut"> <a href="#" class="loginBtn btn btnPrimary boxShadowIn boxShadowOut" id="loginBtn">Login</a></div></form></div><div id="checkResetCode" class="container" style="display: none; transform: translateX(150vw)"><h1>Check Reset Code</h1><form action="#" method="POST"><div class="formControl"><label for="code">Code</label> <input type="text" id="code" name="code"></div><div class="error hidden" id="resetCodeError"><button class="close" type="button">×</button><div></div></div><div class="btnContainer"><input type="submit" value="Submit" class="btn btnPrimary boxShadowIn boxShadowOut"> <a href="#" class="loginBtn btn btnPrimary boxShadowIn boxShadowOut" id="resendEmail">Resend Email</a></div></form></div><div id="changePassword" class="container" style="display: none; transform: translateX(150vw)"><h1>Reset Password</h1><form action="" method="POST"><div class="formControl"><label for="pass">Password</label> <input type="password" name="pass" id="pass"> <i class="fa-solid fa-eye"></i></div><div class="formControl"><label for="rePass">Password</label> <input type="password" name="rePass" id="rePass"> <i class="fa-solid fa-eye"></i></div><div class="error hidden" id="changeError"><button class="close" type="button">×</button><div></div></div><div class="btnContainer"><input type="submit" value="Submit" class="btn btnPrimary boxShadowIn boxShadowOut"> <a href="#" class="loginBtn btn btnPrimary boxShadowIn boxShadowOut">Login</a></div></form></div></main><script src="js/index.js"></script></body></html>
 | 
			
		||||
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Editor</title><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="stylesheet" href="css/main.css"><script src="https://kit.fontawesome.com/ed3c25598e.js" crossorigin="anonymous"></script></head><body><main class="login"><div id="login" class="container shown"><h1>Login To Editor</h1><form action="" method="POST"><div class="formControl"><label for="username">Username</label> <input type="text" id="username" name="username" required></div><div class="formControl passwordControl"><label for="password">Password</label> <input type="password" id="password" name="password" required> <i class="fa-solid fa-eye"></i></div><div class="error hidden" id="loginError"><button class="close" type="button">×</button><div></div></div><div class="btnContainer"><input type="submit" value="Submit" class="btn btnPrimary boxShadowIn boxShadowOut"> <a href="#" id="resetPwd">Reset Password</a></div></form></div><div id="resetPassword" class="container" style="display: none; transform: translateX(150vw)"><h1>Reset Password</h1><form action="#" method="POST"><div class="formControl"><label for="email">Email</label> <input type="email" id="email" name="email"></div><div class="error hidden" id="resetError"><button class="close" type="button">×</button><div></div></div><div class="btnContainer"><input type="submit" value="Submit" class="btn btnPrimary boxShadowIn boxShadowOut"> <a href="#" class="loginBtn btn btnPrimary boxShadowIn boxShadowOut" id="loginBtn">Login</a></div></form></div><div id="checkResetCode" class="container" style="display: none; transform: translateX(150vw)"><h1>Check Reset Code</h1><form action="#" method="POST"><div class="formControl"><label for="code">Code</label> <input type="text" id="code" name="code"></div><div class="error hidden" id="resetCodeError"><button class="close" type="button">×</button><div></div></div><div class="btnContainer"><input type="submit" value="Submit" class="btn btnPrimary boxShadowIn boxShadowOut"> <a href="#" class="loginBtn btn btnPrimary boxShadowIn boxShadowOut" id="resendEmail">Resend Email</a></div></form></div><div id="changePassword" class="container" style="display: none; transform: translateX(150vw)"><h1>Reset Password</h1><form action="" method="POST"><div class="formControl"><label for="pass">Password</label> <input type="password" name="pass" id="pass"> <i class="fa-solid fa-eye"></i></div><div class="formControl"><label for="rePass">Password</label> <input type="password" name="rePass" id="rePass"> <i class="fa-solid fa-eye"></i></div><div class="error hidden" id="changeError"><button class="close" type="button">×</button><div></div></div><div class="btnContainer"><input type="submit" value="Submit" class="btn btnPrimary boxShadowIn boxShadowOut"> <a href="#" class="loginBtn btn btnPrimary boxShadowIn boxShadowOut">Login</a></div></form></div></main><script src="js/index.js"></script></body></html>
 | 
			
		||||
							
								
								
									
										2
									
								
								dist/editor/js/editor.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								dist/editor/js/editor.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1
									
								
								dist/editor/js/main.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								dist/editor/js/main.js
									
									
									
									
										vendored
									
									
								
							@ -1 +0,0 @@
 | 
			
		||||
function showErrorMessage(e){document.querySelector("#loginError").classList.remove("hidden"),document.querySelector("#loginError div").innerText=e}document.addEventListener("DOMContentLoaded",(e=>{fetch("/api/user/isLoggedIn").then((e=>{e.ok&&(window.location.href="./editor.html")}))})),document.querySelector("#login form").addEventListener("submit",(e=>{e.preventDefault();let r=new FormData;if(e.target.username.value.length>0&&e.target.password.value.length>0)return r.append("username",e.target.username.value),r.append("password",e.target.password.value),void fetch("/api/user/login",{method:"POST",body:r}).then((e=>{e.ok?window.location.href="./editor.html":400!==e.status?(document.querySelector("#loginError").classList.remove("hidden"),document.querySelector("#loginError div").innerHTML="Invalid username or password"):showErrorMessage("Please type in a username and password.")}));document.querySelector("#loginError").classList.remove("hidden"),document.querySelector("#loginError div").innerHTML="Please type in a username and password"})),document.querySelector("#loginError .close").addEventListener("click",(()=>document.querySelector("#loginError").classList.toggle("hidden")));
 | 
			
		||||
							
								
								
									
										2
									
								
								dist/js/main.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								dist/js/main.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@ -1,6 +1,5 @@
 | 
			
		||||
<?php /** @noinspection PhpIncludeInspection */
 | 
			
		||||
 | 
			
		||||
session_start();
 | 
			
		||||
////////////////// Index file //////////////
 | 
			
		||||
/// Creates base routes and runs         ///
 | 
			
		||||
/// respective functions                 ///
 | 
			
		||||
@ -54,6 +53,8 @@ $app->get("/timelineData/{timeline}", function (Request $request, Response $resp
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    // something went wrong
 | 
			
		||||
    $response->getBody()->write(json_encode(array("errorMessage" => "Error, timeline data not found")));
 | 
			
		||||
    return $response->withStatus(404);
 | 
			
		||||
@ -80,10 +81,10 @@ $app->patch("/timelineData/{timeline}/{id}", function (Request $request, Respons
 | 
			
		||||
            return $response->withStatus(500);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $response->withStatus(200);
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ($args["timeline"] == "work" && $args["id"] != null)
 | 
			
		||||
    if ($args["timeline"] == "work" && $args["id"] != "undefined")
 | 
			
		||||
    {
 | 
			
		||||
        if (empty($data["dateFrom"]) || empty($data["dateTo"]) || empty($data["companyName"]) || empty($data["area"]) || empty($data["title"]))
 | 
			
		||||
        {
 | 
			
		||||
@ -99,7 +100,7 @@ $app->patch("/timelineData/{timeline}/{id}", function (Request $request, Respons
 | 
			
		||||
            return $response->withStatus(500);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $response->withStatus(200);
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $response->getBody()->write(json_encode(array("error" => "The correct data was not sent")));
 | 
			
		||||
@ -118,7 +119,7 @@ $app->delete("/timelineData/{timeline}/{id}", function (Request $request, Respon
 | 
			
		||||
            return $response->withStatus(500);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $response->withStatus(200);
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ($args["timeline"] == "work" && $args["id"] != null)
 | 
			
		||||
@ -130,7 +131,7 @@ $app->delete("/timelineData/{timeline}/{id}", function (Request $request, Respon
 | 
			
		||||
            return $response->withStatus(500);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $response->withStatus(200);
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $response->getBody()->write(json_encode(array("error" => "The correct data was not sent")));
 | 
			
		||||
@ -159,7 +160,7 @@ $app->post("/timelineData/{timeline}", function (Request $request, Response $res
 | 
			
		||||
        }
 | 
			
		||||
    
 | 
			
		||||
        $response->getBody()->write(json_encode(array("ID" => $insertedID)));
 | 
			
		||||
        return $response->withStatus(200);
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ($args["timeline"] == "work")
 | 
			
		||||
@ -185,7 +186,7 @@ $app->post("/timelineData/{timeline}", function (Request $request, Response $res
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        $response->getBody()->write(json_encode(array("ID" => $insertedID)));
 | 
			
		||||
        return $response->withStatus(200);
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $response->getBody()->write(json_encode(array("error" => "The correct data was not sent")));
 | 
			
		||||
@ -211,6 +212,105 @@ $app->get("/projectData", function (Request $request, Response $response)
 | 
			
		||||
    return $response;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$app->patch("/projectData/{id}", function (Request $request, Response $response, array $args)
 | 
			
		||||
{
 | 
			
		||||
    global $projectData;
 | 
			
		||||
    $data = $request->getParsedBody();
 | 
			
		||||
    if ($args["id"] != "undefined")
 | 
			
		||||
    {
 | 
			
		||||
        if (empty($data["title"]) || empty($data["isMainProject"]) || empty($data["information"]) || empty($data["gitLink"]))
 | 
			
		||||
        {
 | 
			
		||||
            // uh oh sent some empty data
 | 
			
		||||
            $response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
 | 
			
		||||
            return $response->withStatus(400);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!$projectData->updateProjectData($args["id"], $data["title"], $data["isMainProject"], $data["information"], $data["projectLink"], $data["gitLink"]))
 | 
			
		||||
        {
 | 
			
		||||
            // uh oh something went wrong
 | 
			
		||||
            $response->getBody()->write(json_encode(array("error" => "Something went wrong", "data" => $projectData->updateProjectData($args["id"], $data["title"], $data["isMainProject"], $data["information"], $data["projectLink"], $data["gitLink"]))));
 | 
			
		||||
            return $response->withStatus(500);
 | 
			
		||||
        }
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $response->getBody()->write(json_encode(array("error" => "Please provide an ID")));
 | 
			
		||||
    return $response->withStatus(400);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$app->delete("/projectData/{id}", function (Request $request, Response $response, array $args)
 | 
			
		||||
{
 | 
			
		||||
    global $projectData;
 | 
			
		||||
    if ($args["id"] != null)
 | 
			
		||||
    {
 | 
			
		||||
        $message = $projectData->deleteProjectData($args["id"]);
 | 
			
		||||
        if ($message === "error")
 | 
			
		||||
        {
 | 
			
		||||
            // uh oh something went wrong
 | 
			
		||||
            $response->getBody()->write(json_encode(array("error" => "Something went wrong or the project with ID ".$args["id"]."does not exist")));
 | 
			
		||||
            return $response->withStatus(500);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($message === "cannot delete")
 | 
			
		||||
        {
 | 
			
		||||
            //uh oh cannot delete the main project
 | 
			
		||||
            $response->getBody()->write(json_encode(array("error" => "Cannot delete the main project")));
 | 
			
		||||
            return $response->withStatus(409);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $response->getBody()->write(json_encode(array("error" => "Please provide an ID")));
 | 
			
		||||
    return $response->withStatus(400);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$app->post("/projectData", function (Request $request, Response $response)
 | 
			
		||||
{
 | 
			
		||||
    global $projectData;
 | 
			
		||||
    $data = $request->getParsedBody();
 | 
			
		||||
    if (empty($data["title"]) || empty($data["isMainProject"]) || empty($data["information"]) || empty($data["gitLink"]))
 | 
			
		||||
    {
 | 
			
		||||
        // uh oh sent some empty data
 | 
			
		||||
        $response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
 | 
			
		||||
        return $response->withStatus(400);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $insertedID = $projectData->addProjectData($data["title"], $data["isMainProject"], $data["information"], $data["projectLink"], $data["gitLink"]);
 | 
			
		||||
    if (!is_int($insertedID))
 | 
			
		||||
    {
 | 
			
		||||
        // uh oh something went wrong
 | 
			
		||||
        $response->getBody()->write(json_encode(array("error" => "Something went wrong", "message" => $insertedID)));
 | 
			
		||||
        return $response->withStatus(500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $response->getBody()->write(json_encode(array("ID" => $insertedID)));
 | 
			
		||||
    return $response;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$app->post("/projectImage/{id}", function (Request $request, Response $response, array $args)
 | 
			
		||||
{
 | 
			
		||||
    global $projectData;
 | 
			
		||||
    $files = $request->getUploadedFiles();
 | 
			
		||||
    if (empty($args["id"]) || empty($files))
 | 
			
		||||
    {
 | 
			
		||||
        // uh oh only some of the data was sent
 | 
			
		||||
        $response->getBody()->write(json_encode(array("error" => "Only some of the data was sent")));
 | 
			
		||||
        return $response->withStatus(400);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $message = $projectData->uploadImage($args["id"], $files["img"]);
 | 
			
		||||
    if (!is_array($message))
 | 
			
		||||
    {
 | 
			
		||||
        // uh oh something went wrong
 | 
			
		||||
        $response->getBody()->write(json_encode(array("error" => $message)));
 | 
			
		||||
        return $response->withStatus(500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $response->getBody()->write(json_encode($message));
 | 
			
		||||
    return $response;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$app->post("/contact", function (Request $request, Response $response)
 | 
			
		||||
{
 | 
			
		||||
    $data = $request->getParsedBody();
 | 
			
		||||
@ -218,6 +318,7 @@ $app->post("/contact", function (Request $request, Response $response)
 | 
			
		||||
    {
 | 
			
		||||
      $response->getBody()->write(json_encode(array("errorMessage" => "Please fill out all the fields")));
 | 
			
		||||
      return $response->withStatus(400);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    if (!filter_var($data["email"], FILTER_VALIDATE_EMAIL)) 
 | 
			
		||||
@ -409,6 +510,12 @@ $app->post("/user/login", function (Request $request, Response $response)
 | 
			
		||||
    return $response->withStatus(401);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$app->get("/user/logout", function (Request $request, Response $response)
 | 
			
		||||
{
 | 
			
		||||
    session_unset();
 | 
			
		||||
    return $response;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$app->get("/user/isLoggedIn", function (Request $request, Response $response) 
 | 
			
		||||
{
 | 
			
		||||
    global $user;
 | 
			
		||||
@ -507,8 +614,4 @@ $app->post("/user/changePassword", function (Request $request, Response $respons
 | 
			
		||||
    return $response->withStatus(500);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
$app->run();
 | 
			
		||||
 | 
			
		||||
@ -2,9 +2,17 @@
 | 
			
		||||
// middleware
 | 
			
		||||
namespace api;
 | 
			
		||||
 | 
			
		||||
session_start();
 | 
			
		||||
 | 
			
		||||
use Psr\Http\Message\ServerRequestInterface;
 | 
			
		||||
use Psr\Http\Server\RequestHandlerInterface;
 | 
			
		||||
use Slim\App;
 | 
			
		||||
use Selective\SameSiteCookie\SameSiteCookieConfiguration;
 | 
			
		||||
use Selective\SameSiteCookie\SameSiteCookieMiddleware;
 | 
			
		||||
use Slim\Exception\HttpInternalServerErrorException;
 | 
			
		||||
use Slim\Exception\HttpMethodNotAllowedException;
 | 
			
		||||
use Slim\Exception\HttpNotFoundException;
 | 
			
		||||
use Slim\Psr7\Response;
 | 
			
		||||
use Tuupola\Middleware\JwtAuthentication;
 | 
			
		||||
use Tuupola\Middleware\JwtAuthentication\RequestMethodRule;
 | 
			
		||||
use Tuupola\Middleware\JwtAuthentication\RequestPathRule;
 | 
			
		||||
@ -24,6 +32,7 @@ class middleware
 | 
			
		||||
        $this->baseMiddleware($app);
 | 
			
		||||
        $this->sameSiteConfig($app);
 | 
			
		||||
        $this->jwtAuth($app);
 | 
			
		||||
        $this->errorHandling($app);
 | 
			
		||||
        $this->returnAsJSON($app);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -70,7 +79,7 @@ class middleware
 | 
			
		||||
        $app->add(new JwtAuthentication([
 | 
			
		||||
            "rules" => [
 | 
			
		||||
                new RequestPathRule([
 | 
			
		||||
                    "path" => ["/api/projectData", "/api/timeline/[a-z]*", "/api/user/testMethod"],
 | 
			
		||||
                    "path" => ["/api/projectData", "/api/timeline/[a-z]*", "/api/logout"],
 | 
			
		||||
                    "ignore" => ["/api/contact", "/api/user/login", "/api/user/changePassword"]
 | 
			
		||||
                ]),
 | 
			
		||||
                new RequestMethodRule([
 | 
			
		||||
@ -86,7 +95,37 @@ class middleware
 | 
			
		||||
                return $response->withStatus(401);
 | 
			
		||||
            }
 | 
			
		||||
        ]));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function errorHandling(App $app): void
 | 
			
		||||
    {
 | 
			
		||||
        $app->add(function (ServerRequestInterface $request, RequestHandlerInterface $handler)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                return $handler->handle($request);
 | 
			
		||||
            }
 | 
			
		||||
            catch (HttpNotFoundException $httpException)
 | 
			
		||||
            {
 | 
			
		||||
                $response = (new Response())->withStatus(404);
 | 
			
		||||
                $response->getBody()->write(json_encode(array("status" => "404", "message" => $request->getUri()->getPath() . " not found")));
 | 
			
		||||
                return $response;
 | 
			
		||||
            }
 | 
			
		||||
            catch (HttpMethodNotAllowedException $httpException)
 | 
			
		||||
            {
 | 
			
		||||
                $response = (new Response())->withStatus(405);
 | 
			
		||||
                $response->getBody()->write(json_encode(array("status" => "405", "message" => "Method not allowed")));
 | 
			
		||||
                return $response;
 | 
			
		||||
            }
 | 
			
		||||
            catch (HttpInternalServerErrorException $exception)
 | 
			
		||||
            {
 | 
			
		||||
                $response = (new Response())->withStatus(500);
 | 
			
		||||
                $response->getBody()->write(json_encode(array("status" => "500", "message" => $exception->getMessage())));
 | 
			
		||||
                return $response;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        $app->addErrorMiddleware(true, true, true);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace api;
 | 
			
		||||
use PDO;
 | 
			
		||||
use Psr\Http\Message\UploadedFileInterface;
 | 
			
		||||
 | 
			
		||||
require_once "./config.php";
 | 
			
		||||
 | 
			
		||||
@ -17,7 +18,7 @@ class projectData
 | 
			
		||||
    function getProjectData(): array
 | 
			
		||||
    {
 | 
			
		||||
        $conn = dbConn();
 | 
			
		||||
        $stmt = $conn->prepare("SELECT title, isMainProject, information, imgLocation, projectLink, githubLink FROM projects order by date LIMIT 4;");
 | 
			
		||||
        $stmt = $conn->prepare("SELECT ID, title, isMainProject, information, imgLocation, projectLink, gitLink FROM projects ORDER BY isMainProject DESC;");
 | 
			
		||||
        $stmt->execute();
 | 
			
		||||
 | 
			
		||||
        // set the resulting array to associative
 | 
			
		||||
@ -27,6 +28,182 @@ class projectData
 | 
			
		||||
        {
 | 
			
		||||
            return $result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return array("errorMessage" => "Error, project data not found");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Update project data in the database with the given ID
 | 
			
		||||
     * @param string $ID - ID of the project in the database to update
 | 
			
		||||
     * @param string $title - Title of the project
 | 
			
		||||
     * @param string $isMainProject - Is the project a main project or not
 | 
			
		||||
     * @param string $information - Information about the project
 | 
			
		||||
     * @param string $projectLink - Link to the project
 | 
			
		||||
     * @param string $gitLink - Link to the git repository
 | 
			
		||||
     * @return bool - True if project was updated, false if not and there was an error
 | 
			
		||||
     */
 | 
			
		||||
    function updateProjectData(string $ID, string $title, string $isMainProject, string $information, string $projectLink, string $gitLink): bool
 | 
			
		||||
    {
 | 
			
		||||
        $conn = dbConn();
 | 
			
		||||
 | 
			
		||||
        if ($isMainProject === "true")
 | 
			
		||||
        {
 | 
			
		||||
            $stmtMainProject = $conn->prepare("UPDATE projects SET isMainProject = 0;");
 | 
			
		||||
            $stmtMainProject->execute();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $stmt = $conn->prepare("UPDATE projects SET title = :title, isMainProject = :isMainProject, information = :information,  projectLink = :projectLink, gitLink = :gitLink WHERE ID = :ID");
 | 
			
		||||
        $stmt->bindParam(":title", $title);
 | 
			
		||||
        $isMainProj = ($isMainProject) ? 1 : 0;
 | 
			
		||||
        $stmt->bindParam(":isMainProject", $isMainProj);
 | 
			
		||||
        $stmt->bindParam(":information", $information);
 | 
			
		||||
        $stmt->bindParam(":projectLink", $projectLink);
 | 
			
		||||
        $stmt->bindParam(":gitLink", $gitLink);
 | 
			
		||||
        $stmt->bindParam(":ID", $ID);
 | 
			
		||||
        return $stmt->execute();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete project data from the database
 | 
			
		||||
     * @param int $ID - ID of the project in the database to delete
 | 
			
		||||
     * @return string - True if project was deleted, false if not and there was an error
 | 
			
		||||
     */
 | 
			
		||||
    function deleteProjectData(int $ID): string
 | 
			
		||||
    {
 | 
			
		||||
        $conn = dbConn();
 | 
			
		||||
 | 
			
		||||
        // check if the project is a main project if it is return false
 | 
			
		||||
 | 
			
		||||
        $stmtMainProject = $conn->prepare("SELECT isMainProject FROM projects WHERE ID = :ID");
 | 
			
		||||
        $stmtMainProject->bindParam(":ID", $ID);
 | 
			
		||||
        $stmtMainProject->execute();
 | 
			
		||||
        $result = $stmtMainProject->fetch(PDO::FETCH_ASSOC);
 | 
			
		||||
 | 
			
		||||
        if ($result["isMainProject"] === "1")
 | 
			
		||||
        {
 | 
			
		||||
            return "cannot delete";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $this->deleteImage($ID);
 | 
			
		||||
 | 
			
		||||
        $stmt = $conn->prepare("DELETE FROM projects WHERE ID = :ID");
 | 
			
		||||
        $stmt->bindParam(":ID", $ID);
 | 
			
		||||
        $stmt->execute();
 | 
			
		||||
 | 
			
		||||
        if ($stmt->rowCount() > 0)
 | 
			
		||||
        {
 | 
			
		||||
            return "ok";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return "error";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Add project data to the database
 | 
			
		||||
     * @param string $title - Title of the project
 | 
			
		||||
     * @param string $isMainProject - Is the project a main project or not
 | 
			
		||||
     * @param string $information - Information about the project
 | 
			
		||||
     * @param string $projectLink - Link to the project
 | 
			
		||||
     * @param string $gitLink - Link to the github repository
 | 
			
		||||
     * @return int|bool - ID of the project if it was added, false if not and there was an error
 | 
			
		||||
     */
 | 
			
		||||
    function addProjectData(string $title, string $isMainProject, string $information, string $projectLink, string $gitLink): int|bool
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $conn = dbConn();
 | 
			
		||||
        if ($isMainProject === "true")
 | 
			
		||||
        {
 | 
			
		||||
            $stmtMainProject = $conn->prepare("UPDATE projects SET isMainProject = 0;");
 | 
			
		||||
            $stmtMainProject->execute();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $stmt = $conn->prepare("INSERT INTO projects (title, isMainProject, information, projectLink, gitLink) VALUES (:title, :isMainProject, :information, :projectLink, :gitLink)");
 | 
			
		||||
        $stmt->bindParam(":title", $title);
 | 
			
		||||
        $isMainProj = ($isMainProject) ? 1 : 0;
 | 
			
		||||
        $stmt->bindParam(":isMainProject", $isMainProj);
 | 
			
		||||
        $stmt->bindParam(":information", $information);
 | 
			
		||||
        $stmt->bindParam(":projectLink", $projectLink);
 | 
			
		||||
        $stmt->bindParam(":gitLink", $gitLink);
 | 
			
		||||
        $stmt->execute();
 | 
			
		||||
 | 
			
		||||
        if ($stmt->rowCount() > 0)
 | 
			
		||||
        {
 | 
			
		||||
            return $conn->lastInsertId();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Upload the image to the server and update the database with the new image location
 | 
			
		||||
     * @param int $ID - ID of the project in the database to update
 | 
			
		||||
     * @param UploadedFileInterface $img - Image preview of the project
 | 
			
		||||
     * @return string|array - String with error message or array with the new image location
 | 
			
		||||
     */
 | 
			
		||||
    public function uploadImage(int $ID, UploadedFileInterface $img): string | array
 | 
			
		||||
    {
 | 
			
		||||
        $targetDir = "../imgs/projects/";
 | 
			
		||||
        $targetFile = $targetDir . basename($img->getClientFilename());
 | 
			
		||||
        $uploadOk = 1;
 | 
			
		||||
        $imageFileType = strtolower(pathinfo($targetFile, PATHINFO_EXTENSION));
 | 
			
		||||
 | 
			
		||||
        // Check if file already exists
 | 
			
		||||
        if (file_exists($targetFile))
 | 
			
		||||
        {
 | 
			
		||||
            return "The file already exists";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check file size
 | 
			
		||||
        if ($img->getSize() > 2000000)
 | 
			
		||||
        {
 | 
			
		||||
            return "The file is too large, max 2MB";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Allow certain file formats
 | 
			
		||||
        if ($imageFileType != "jpg" && $imageFileType != "png" && $imageFileType != "jpeg" && $imageFileType != "gif")
 | 
			
		||||
        {
 | 
			
		||||
            return "Only JPG, JPEG, PNG & GIF files are allowed";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $img->moveTo($targetFile);
 | 
			
		||||
 | 
			
		||||
        if (file_exists($targetFile))
 | 
			
		||||
        {
 | 
			
		||||
            $this->deleteImage($ID);
 | 
			
		||||
            // update the database with the new image location
 | 
			
		||||
            $conn = dbConn();
 | 
			
		||||
            $stmt = $conn->prepare("UPDATE projects SET imgLocation = :imgLocation WHERE ID = :ID");
 | 
			
		||||
            $stmt->bindParam(":imgLocation", $targetFile);
 | 
			
		||||
            $stmt->bindParam(":ID", $ID);
 | 
			
		||||
            $stmt->execute();
 | 
			
		||||
 | 
			
		||||
            if ($stmt->rowCount() > 0)
 | 
			
		||||
            {
 | 
			
		||||
                return array("imgLocation" => $targetFile);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return "Couldn't update the database";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return "Couldn't upload the image";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Delete the image from the server
 | 
			
		||||
     * @param int $ID - ID of the project in the database
 | 
			
		||||
     */
 | 
			
		||||
    private function deleteImage(int $ID): void
 | 
			
		||||
    {
 | 
			
		||||
        $conn = dbConn();
 | 
			
		||||
        $imgStmt = $conn->prepare("SELECT imgLocation FROM projects WHERE ID = :ID");
 | 
			
		||||
        $imgStmt->bindParam(":ID", $ID);
 | 
			
		||||
        $imgStmt->execute();
 | 
			
		||||
        $imgLocation = $imgStmt->fetch(PDO::FETCH_ASSOC)["imgLocation"];
 | 
			
		||||
 | 
			
		||||
        if ($imgLocation != null)
 | 
			
		||||
        {
 | 
			
		||||
            unlink($imgLocation);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -69,7 +69,7 @@ section#projects .otherProj > div .oProjItem {
 | 
			
		||||
    -webkit-border-radius: 10px;
 | 
			
		||||
    -moz-border-radius: 10px;
 | 
			
		||||
    border-radius: 10px;
 | 
			
		||||
    padding: 0 1em;
 | 
			
		||||
    padding: 0.75em 1em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section#projects .otherProj > div .oProjItem:nth-child(2) {
 | 
			
		||||
 | 
			
		||||
@ -121,25 +121,6 @@ a.btn:active, form input[type="submit"]:active {
 | 
			
		||||
    text-shadow: 0 6px 4px var(--mutedBlack);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form .formControl {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form .formControl input:not([type="submit"]), form .formControl textarea {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    border: 4px solid var(--primaryDefault);
 | 
			
		||||
    background: none;
 | 
			
		||||
    outline: none;
 | 
			
		||||
    -webkit-border-radius: 1em;
 | 
			
		||||
    -moz-border-radius: 1em;
 | 
			
		||||
    border-radius: 0.5em;
 | 
			
		||||
    padding: 0 0.5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form .formControl textarea {
 | 
			
		||||
    padding: 0.5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form .formControl input:not([type="submit"]).invalid:invalid, form .formControl textarea.invalid:invalid {
 | 
			
		||||
    border: 4px solid var(--errorDefault);
 | 
			
		||||
}
 | 
			
		||||
@ -159,6 +140,14 @@ form .formControl input:not([type="submit"]) {
 | 
			
		||||
 | 
			
		||||
form .formControl {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    justify-content: flex-start;
 | 
			
		||||
    align-items: flex-start;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form .formControl.passwordControl {
 | 
			
		||||
    display: block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form input[type="submit"] {
 | 
			
		||||
@ -189,7 +178,8 @@ form .formControl input:not([type="submit"]).invalid:invalid:focus, form .formCo
 | 
			
		||||
    box-shadow: 0 4px 2px 0 var(--mutedBlack);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form .formControl input:not([type="submit"]):focus, form .formControl textarea:focus {
 | 
			
		||||
form .formControl input:not([type="submit"]):focus, form .formControl textarea:focus,
 | 
			
		||||
form .formControl input:not([type="submit"]):hover, form .formControl textarea:hover {
 | 
			
		||||
    border: 4px solid var(--primaryHover);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -208,6 +198,91 @@ form .formControl input:not([type="submit"]):focus + i.fa-eye-slash {
 | 
			
		||||
    color: var(--primaryHover);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form .formControl .checkContainer {
 | 
			
		||||
    display: block;
 | 
			
		||||
    position: relative;
 | 
			
		||||
    margin-bottom: 1.25em;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    -webkit-user-select: none;
 | 
			
		||||
    -moz-user-select: none;
 | 
			
		||||
    -ms-user-select: none;
 | 
			
		||||
    user-select: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form .formControl .checkContainer input {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    opacity: 0;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    height: 0;
 | 
			
		||||
    width: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form .formControl .checkContainer .checkmark {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: 1.25em;
 | 
			
		||||
    left: 0;
 | 
			
		||||
    height: 25px;
 | 
			
		||||
    width: 25px;
 | 
			
		||||
    background-color: var(--mutedGrey);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form .formControl .checkContainer:hover input ~ .checkmark {
 | 
			
		||||
    background-color: var(--grey);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form .formControl .checkContainer input:checked ~ .checkmark {
 | 
			
		||||
    background-color: var(--primaryDefault);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form .formControl .checkContainer input:checked:hover ~ .checkmark {
 | 
			
		||||
    background-color: var(--primaryHover);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form .formControl .checkContainer .checkmark:after {
 | 
			
		||||
    content: "";
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form .formControl .checkContainer input:checked ~ .checkmark:after {
 | 
			
		||||
    display: block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form .formControl .checkContainer .checkmark:after {
 | 
			
		||||
    left: 9px;
 | 
			
		||||
    top: 5px;
 | 
			
		||||
    width: 5px;
 | 
			
		||||
    height: 10px;
 | 
			
		||||
    border: solid white;
 | 
			
		||||
    border-width: 0 3px 3px 0;
 | 
			
		||||
    -webkit-transform: rotate(45deg);
 | 
			
		||||
    -ms-transform: rotate(45deg);
 | 
			
		||||
    transform: rotate(45deg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form .formControl input[type="file"] {
 | 
			
		||||
    padding: 0;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form .formControl input[type="file"]::file-selector-button {
 | 
			
		||||
    background-color: var(--primaryDefault);
 | 
			
		||||
    color: #FFFFFF;
 | 
			
		||||
    border: 0;
 | 
			
		||||
    border-right: 5px solid var(--mutedBlack);
 | 
			
		||||
    padding: 15px;
 | 
			
		||||
    margin-right: 20px;
 | 
			
		||||
    -webkit-transition: all .5s;
 | 
			
		||||
    -moz-transition: all .5s;
 | 
			
		||||
    -ms-transition: all .5s;
 | 
			
		||||
    -o-transition: all .5s;
 | 
			
		||||
    transition: all .5s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
form .formControl input[type="file"]:hover::file-selector-button {
 | 
			
		||||
    background-color: var(--primaryHover);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section#about, section#curriculumVitae h1 {
 | 
			
		||||
    padding: 0 5rem;
 | 
			
		||||
}
 | 
			
		||||
@ -1,5 +1,9 @@
 | 
			
		||||
/*** Main editor styles ***/
 | 
			
		||||
 | 
			
		||||
textarea {
 | 
			
		||||
    resize: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section#curriculumVitae, section#projects, section#settings {
 | 
			
		||||
    margin: 0 2em;
 | 
			
		||||
}
 | 
			
		||||
@ -23,25 +27,34 @@ input[type="submit"] {
 | 
			
		||||
	font-weight: 400;	
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div.editorContainer {
 | 
			
		||||
div.editorContainer, div.projectsGrid {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: row;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    align-items: baseline;
 | 
			
		||||
    align-items: flex-start;
 | 
			
		||||
    gap: 2em;
 | 
			
		||||
    margin-bottom: 0.5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div.editorContainer > * {
 | 
			
		||||
div.editorContainer > *, div.projectsGrid > * {
 | 
			
		||||
    width: 45%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section#projects {
 | 
			
		||||
    display: block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section#curriculumVitae, section#settings {
 | 
			
		||||
    display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div.modifyBtnContainer {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: row;
 | 
			
		||||
    justify-content: space-between;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    margin-bottom: 0.5em;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
div.dateContainer, div.companyAreaContainer {
 | 
			
		||||
@ -62,14 +75,17 @@ section#curriculumVitae .timeline {
 | 
			
		||||
    height: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section#curriculumVitae .timelineItem {
 | 
			
		||||
    color: #FFFFFF;
 | 
			
		||||
    border: 2px solid var(--timelineItemBrdr);
 | 
			
		||||
section#curriculumVitae .timelineItem, section#projects .projItem {
 | 
			
		||||
    -webkit-border-radius: 10px;
 | 
			
		||||
    -moz-border-radius: 10px;
 | 
			
		||||
    border-radius: 10px;
 | 
			
		||||
    padding: 0 1rem;
 | 
			
		||||
    padding: 1rem;
 | 
			
		||||
    position: relative;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section#curriculumVitae .timelineItem {
 | 
			
		||||
    border: 2px solid var(--timelineItemBrdr);
 | 
			
		||||
    color: #FFFFFF;
 | 
			
		||||
    background-color: var(--primaryHover);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -77,6 +93,9 @@ section#curriculumVitae .timelineItem.editing {
 | 
			
		||||
    color: #000000;
 | 
			
		||||
    border: 5px solid var(--primaryDefault);
 | 
			
		||||
    padding: 0.5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section#curriculumVitae .timelineItem.editing {
 | 
			
		||||
    background-color: #FFFFFF;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -109,12 +128,15 @@ section#curriculumVitae form.timelineItem.editing div.gradeContainer.formControl
 | 
			
		||||
section#curriculumVitae form.timelineItem:not(.editing) div.gradeContainer.formControl input,
 | 
			
		||||
section#curriculumVitae form.timelineItem:not(.editing) div.companyAreaContainer.formControl input,
 | 
			
		||||
section#curriculumVitae form.timelineItem:not(.editing) .formControl .courseText,
 | 
			
		||||
section#curriculumVitae form.timelineItem:not(.editing) .formControl .jobTitleText {
 | 
			
		||||
section#curriculumVitae form.timelineItem:not(.editing) .formControl .jobTitleText,
 | 
			
		||||
section#projects form.projItem:not(.editing) div.formControl.projectTitleContainer input,
 | 
			
		||||
section#projects form.projItem:not(.editing) div.formControl.infoContainer textarea {
 | 
			
		||||
    outline: none;
 | 
			
		||||
    border: none;
 | 
			
		||||
    -webkit-border-radius: 0;
 | 
			
		||||
    -moz-border-radius: 0;
 | 
			
		||||
    border-radius: 0;
 | 
			
		||||
    resize: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section#curriculumVitae form.timelineItem:not(.editing) div.gradeContainer.formControl > *,
 | 
			
		||||
@ -147,3 +169,67 @@ section#curriculumVitae form.timelineItem:not(.editing) input[type=submit] {
 | 
			
		||||
section#curriculumVitae form.timelineItem:not(.editing) div.companyAreaContainer input {
 | 
			
		||||
    width: 30%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section#projects #projList .projItem {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    margin: 0 auto;
 | 
			
		||||
    width: 90%;
 | 
			
		||||
    border: 1px solid var(--grey);
 | 
			
		||||
    gap: 1em;
 | 
			
		||||
    box-shadow: 0 6px 4px 0 var(--mutedBlack);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section#projects #projList {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    gap: 1em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section#projects #projList .projItem img {
 | 
			
		||||
    max-width: 15rem;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    padding: 0 1em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section#projects .projItem .linkContainer {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: row;
 | 
			
		||||
    justify-content: flex-start;
 | 
			
		||||
    gap: 3em;
 | 
			
		||||
    margin-left: 2em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section#projects .linkContainer .btn {
 | 
			
		||||
    padding: 0.25em 0.5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section#projects #isMainProject {
 | 
			
		||||
    width: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section#projects form.projItem:not(.editing) div.formControl.imageContainer,
 | 
			
		||||
section#projects form.projItem:not(.editing) div.formControl.isMainProject,
 | 
			
		||||
section#projects form.projItem:not(.editing) div.formControl.viewProjContainer,
 | 
			
		||||
section#projects form.projItem:not(.editing) div.formControl.gitContainer,
 | 
			
		||||
section#projects form.projItem:not(.editing) input[type="submit"],
 | 
			
		||||
section#projects form.projItem.editing img.displayedImage,
 | 
			
		||||
section#projects form.projItem.editing div.linkContainer {
 | 
			
		||||
    display: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section#projects form.projItem:not(.editing) div.formControl.projectTitleContainer input {
 | 
			
		||||
    font-size: 1.17em;
 | 
			
		||||
    /*margin: 1em 0;*/
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
section#projects form.projItem:not(.editing) div.formControl.projectTitleContainer input,
 | 
			
		||||
section#projects form.projItem:not(.editing) div.formControl.infoContainer textarea{
 | 
			
		||||
    color: #000000;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
    <title>Editor</title>
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
    <link rel="stylesheet" href="css/main.css">
 | 
			
		||||
    <script src="https://kit.fontawesome.com/ed3c25598e.js" crossorigin="anonymous"></script>
 | 
			
		||||
</head>
 | 
			
		||||
@ -10,9 +11,9 @@
 | 
			
		||||
    <nav class="sideNav">
 | 
			
		||||
        <a href="#" class="closeBtn" id="navClose">×</a>
 | 
			
		||||
        <ul>
 | 
			
		||||
            <li><a href="#" class="active"><span><</span>CV<span>></span></a></li>
 | 
			
		||||
            <li><a href="#"><span><</span>Projects<span>></span></a></li>
 | 
			
		||||
            <li><a href="#"><span><</span>Settings<span>></span></a></li>
 | 
			
		||||
            <li><a href="#" id="goToCV"><span><</span>CV<span>></span></a></li>
 | 
			
		||||
            <li><a href="#" id="goToProjects" class="active"><span><</span>Projects<span>></span></a></li>
 | 
			
		||||
            <li><a href="#" id="logout"><span><</span>Logout<span>></span></a></li>
 | 
			
		||||
        </ul>
 | 
			
		||||
    </nav>
 | 
			
		||||
    <main class="editor" style="margin-left: 250px;">
 | 
			
		||||
@ -21,6 +22,7 @@
 | 
			
		||||
            
 | 
			
		||||
            <h1>Editor</h1>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <section id="curriculumVitae">
 | 
			
		||||
            <h2>curriculum vitae</h2>
 | 
			
		||||
            <div class="cvGrid">
 | 
			
		||||
@ -31,22 +33,22 @@
 | 
			
		||||
                        <form action="" method="POST" id="addEdu">
 | 
			
		||||
                            <div class="formControl">
 | 
			
		||||
                                <label for="dateFromE">Date From</label>
 | 
			
		||||
                                <input type="date" id="dateFromE" name="dateFromE">
 | 
			
		||||
                                <input type="date" id="dateFromE" name="dateFromE" required>
 | 
			
		||||
                            </div>
 | 
			
		||||
 | 
			
		||||
                            <div class="formControl">
 | 
			
		||||
                                <label for="dateToE">Date To</label>
 | 
			
		||||
                                <input type="date" id="dateToE" name="dateToE">
 | 
			
		||||
                                <input type="date" id="dateToE" name="dateToE" required>
 | 
			
		||||
                            </div>
 | 
			
		||||
 | 
			
		||||
                            <div class="formControl">
 | 
			
		||||
                                <label for="grade">Grade</label>
 | 
			
		||||
                                <input type="text" id="grade" name="grade">
 | 
			
		||||
                                <input type="text" id="grade" name="grade" required>
 | 
			
		||||
                            </div>
 | 
			
		||||
 | 
			
		||||
                            <div class="formControl">
 | 
			
		||||
                                <label for="courseTitle">Course Title</label>
 | 
			
		||||
                                <input type="text" id="courseTitle" name="courseTitle">
 | 
			
		||||
                                <input type="text" id="courseTitle" name="courseTitle" required>
 | 
			
		||||
                            </div>
 | 
			
		||||
 | 
			
		||||
                            <div class="error hidden" id="eduError">
 | 
			
		||||
@ -69,7 +71,7 @@
 | 
			
		||||
                        <form action="" method="POST" id="addWork">
 | 
			
		||||
                            <div class="formControl">
 | 
			
		||||
                                <label for="dateFromW">Date From</label>
 | 
			
		||||
                                <input type="date" id="dateFromW" name="dateFromW">
 | 
			
		||||
                                <input type="date" id="dateFromW" name="dateFromW" required>
 | 
			
		||||
                            </div>
 | 
			
		||||
 | 
			
		||||
                            <div class="formControl">
 | 
			
		||||
@ -79,17 +81,17 @@
 | 
			
		||||
 | 
			
		||||
                            <div class="formControl">
 | 
			
		||||
                                <label for="company">Company</label>
 | 
			
		||||
                                <input type="text" id="company" name="company">
 | 
			
		||||
                                <input type="text" id="company" name="company" required>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            
 | 
			
		||||
                            <div class="formControl">
 | 
			
		||||
                                <label for="area">Area</label>
 | 
			
		||||
                                <input type="text" id="area" name="area">
 | 
			
		||||
                                <input type="text" id="area" name="area" required>
 | 
			
		||||
                            </div>
 | 
			
		||||
 | 
			
		||||
                            <div class="formControl">
 | 
			
		||||
                                <label for="jobTitle">Job Title</label>
 | 
			
		||||
                                <input type="text" id="jobTitle" name="jobTitle">
 | 
			
		||||
                                <input type="text" id="jobTitle" name="jobTitle" required>
 | 
			
		||||
                            </div>
 | 
			
		||||
 | 
			
		||||
                            <div class="error hidden" id="workError">
 | 
			
		||||
@ -107,6 +109,48 @@
 | 
			
		||||
            </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </section>
 | 
			
		||||
 | 
			
		||||
        <section id="projects">
 | 
			
		||||
            <h2>projects</h2>
 | 
			
		||||
            <div class="projectsGrid">
 | 
			
		||||
                <form action="" id="addProj">
 | 
			
		||||
                    <div class="formControl">
 | 
			
		||||
                        <label for="projTitle">Title </label>
 | 
			
		||||
                        <input type="text" name="projTitle" id="projTitle" required>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="formControl">
 | 
			
		||||
                        <label class="checkContainer" for="isMainProject">Is It The Main Project
 | 
			
		||||
                            <input type="checkbox" id="isMainProject" name="isMainProject">
 | 
			
		||||
                            <span class="checkmark"></span>
 | 
			
		||||
                        </label>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="formControl">
 | 
			
		||||
                        <label for="projImg">Image</label>
 | 
			
		||||
                        <input type="file" name="projImg" id="projImg">
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="formControl">
 | 
			
		||||
                        <label for="projInfo">Description</label>
 | 
			
		||||
                        <textarea name="projInfo" id="projInfo" required></textarea>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="formControl">
 | 
			
		||||
                        <label for="projLink">Project Link</label>
 | 
			
		||||
                        <input type="text" name="projLink" id="projLink" pattern="https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)">
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="formControl">
 | 
			
		||||
                        <label for="gitLink">Git Link</label>
 | 
			
		||||
                        <input type="text" name="gitLink" id="gitLink" pattern="https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)" required>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="error hidden" id="projError">
 | 
			
		||||
                        <button class="close" type="button">×</button>
 | 
			
		||||
                        <div></div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <input type="submit" value="Add new Project" class="btn btnPrimary boxShadowIn boxShadowOut">
 | 
			
		||||
                </form>
 | 
			
		||||
                <div id="projList">
 | 
			
		||||
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </section>
 | 
			
		||||
    </main>
 | 
			
		||||
    
 | 
			
		||||
    <script src="js/editor.js"></script>
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@
 | 
			
		||||
                <input type="text" id="username" name="username" required>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div class="formControl">
 | 
			
		||||
            <div class="formControl passwordControl">
 | 
			
		||||
                <label for="password">Password</label>
 | 
			
		||||
                <input type="password" id="password" name="password" required>
 | 
			
		||||
                <i class="fa-solid fa-eye"></i>
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
let dateOptions = {month: 'short', year: 'numeric'};
 | 
			
		||||
let textareaLoaded = false;
 | 
			
		||||
 | 
			
		||||
document.addEventListener('DOMContentLoaded', () =>
 | 
			
		||||
{
 | 
			
		||||
@ -13,7 +14,6 @@ document.addEventListener('DOMContentLoaded', () =>
 | 
			
		||||
    document.querySelector("#dateFromE").max = new Date().toISOString().split("T")[0];
 | 
			
		||||
    document.querySelector("#dateFromW").max = new Date().toISOString().split("T")[0];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    fetch("/api/timelineData/edu").then(res =>
 | 
			
		||||
    {
 | 
			
		||||
        res.json().then(json =>
 | 
			
		||||
@ -46,7 +46,45 @@ document.addEventListener('DOMContentLoaded', () =>
 | 
			
		||||
            document.querySelector("#edu").innerHTML = "No education data found";
 | 
			
		||||
        })
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    fetch("/api/projectData").then(res =>
 | 
			
		||||
    {
 | 
			
		||||
        res.json().then(json =>
 | 
			
		||||
        {
 | 
			
		||||
            if (res.ok)
 | 
			
		||||
            {
 | 
			
		||||
                json.forEach(item =>
 | 
			
		||||
                {
 | 
			
		||||
                    addProject(item["ID"], (item["isMainProject"] === "1" ? "true" : "false"), (item["imgLocation"] === "") ?  "../imgs/placeholder.png" : item["imgLocation"], item["title"], item["information"], item["projectLink"], item["gitLink"]);
 | 
			
		||||
                })
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            document.querySelector("#projList").innerHTML = "No project data found";
 | 
			
		||||
        })
 | 
			
		||||
    })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
document.querySelector("body").addEventListener("click", () =>
 | 
			
		||||
{
 | 
			
		||||
    if (textareaLoaded)
 | 
			
		||||
    {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const tx = document.querySelectorAll("main.editor textarea");
 | 
			
		||||
    console.log(tx)
 | 
			
		||||
    for (let i = 0; i < tx.length; i++)
 | 
			
		||||
    {
 | 
			
		||||
        console.log("height: " + tx[i].scrollHeight + "px");
 | 
			
		||||
        tx[i].setAttribute("style", "height:" + (tx[i].scrollHeight) + "px;overflow-y:hidden;");
 | 
			
		||||
        tx[i].oninput = e =>
 | 
			
		||||
        {
 | 
			
		||||
            e.target.style.height = "0";
 | 
			
		||||
            e.target.style.height = (e.target.scrollHeight) + "px";
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
    textareaLoaded = true;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
document.querySelector("#navOpen").addEventListener("click", e =>
 | 
			
		||||
{
 | 
			
		||||
@ -75,7 +113,7 @@ document.querySelector("#addEdu").addEventListener("submit", e =>
 | 
			
		||||
        method: "POST",
 | 
			
		||||
        body: data,
 | 
			
		||||
        headers: {
 | 
			
		||||
            "Authentication": localStorage.getItem("token")
 | 
			
		||||
            "Authorization": "Bearer " + localStorage.getItem("token")
 | 
			
		||||
        }
 | 
			
		||||
    }).then(res => res.json().then(json => 
 | 
			
		||||
    {
 | 
			
		||||
@ -92,8 +130,7 @@ document.querySelector("#addEdu").addEventListener("submit", e =>
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        document.querySelector("#eduError").classList.remove("hidden");
 | 
			
		||||
        document.querySelector("#eduError div").innerHTML = json.error;
 | 
			
		||||
        showErrorMessage(json.error, "edu");
 | 
			
		||||
    }));
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -111,7 +148,7 @@ document.querySelector("#addWork").addEventListener("submit", e =>
 | 
			
		||||
        method: "POST",
 | 
			
		||||
        body: data,
 | 
			
		||||
        headers: {
 | 
			
		||||
            "Authentication": localStorage.getItem("token")
 | 
			
		||||
            "Authorization": "Bearer " + localStorage.getItem("token")
 | 
			
		||||
        }
 | 
			
		||||
    }).then(res => res.json().then(json =>
 | 
			
		||||
    {
 | 
			
		||||
@ -119,7 +156,76 @@ document.querySelector("#addWork").addEventListener("submit", e =>
 | 
			
		||||
        {
 | 
			
		||||
            let endPeriod = data.get("dateTo") === null ? "Present" : data.get("dateTo ");
 | 
			
		||||
            addWorkData(json.ID, data.get("dateFrom"), endPeriod, data.get("companyName"), data.get("area"), data.get("title"), true);
 | 
			
		||||
            document.querySelector("#addEdu").reset();
 | 
			
		||||
            document.querySelector("#addWork").reset();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (res.status === 401)
 | 
			
		||||
        {
 | 
			
		||||
            window.location.href = "./";
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        showErrorMessage(json.error, "work");
 | 
			
		||||
    }));
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
document.querySelector("#addProj").addEventListener("submit", e =>
 | 
			
		||||
{
 | 
			
		||||
    e.preventDefault();
 | 
			
		||||
    let data = new FormData();
 | 
			
		||||
    data.append("title", document.querySelector("#projTitle").value);
 | 
			
		||||
    data.append("isMainProject", document.querySelector("#isMainProject").checked ? "true" : "false");
 | 
			
		||||
    data.append("information", document.querySelector("#projInfo").value);
 | 
			
		||||
    data.append("projectLink", (document.querySelector("#projLink").value) ? document.querySelector("#projLink").value : "N/A");
 | 
			
		||||
    data.append("gitLink", document.querySelector("#gitLink").value);
 | 
			
		||||
 | 
			
		||||
    let imgData = new FormData();
 | 
			
		||||
    imgData.append("img", document.querySelector("#projImg").files[0]);
 | 
			
		||||
 | 
			
		||||
    let newProjectID = 0;
 | 
			
		||||
 | 
			
		||||
    fetch("/api/projectData", {
 | 
			
		||||
        method: "POST",
 | 
			
		||||
        body: data,
 | 
			
		||||
        headers: {
 | 
			
		||||
            "Authorization": "Bearer " + localStorage.getItem("token")
 | 
			
		||||
        }
 | 
			
		||||
    }).then(res => res.json().then(newProjectData =>
 | 
			
		||||
    {
 | 
			
		||||
        if (res.ok)
 | 
			
		||||
        {
 | 
			
		||||
            if (imgData.get("img") === "undefined")
 | 
			
		||||
            {
 | 
			
		||||
                addProject(newProjectData.ID, data.get("isMainProject"),"../imgs/placeholder.png", data.get("title"), data.get("information"), data.get("projectLink"), data.get("gitLink"));
 | 
			
		||||
                document.querySelector("#addProj").reset();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            newProjectID = newProjectData.ID;
 | 
			
		||||
 | 
			
		||||
            return fetch("/api/projectImage/" + newProjectData.ID, {
 | 
			
		||||
                method: "POST",
 | 
			
		||||
                body: imgData,
 | 
			
		||||
                headers: {
 | 
			
		||||
                    "Authorization": "Bearer " + localStorage.getItem("token")
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (res.status === 401)
 | 
			
		||||
        {
 | 
			
		||||
            window.location.href = "./";
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        showErrorMessage(newProjectData.error, "proj");
 | 
			
		||||
 | 
			
		||||
    }).then(res => res.json().then(newProjectImage =>
 | 
			
		||||
    {
 | 
			
		||||
        if (res.ok)
 | 
			
		||||
        {
 | 
			
		||||
            addProject(newProjectID, data.get("isMainProject"), newProjectImage.imgLocation, data.get("title"), data.get("information"), data.get("projectLink"), data.get("gitLink"));
 | 
			
		||||
            document.querySelector("#addProj").reset();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -129,38 +235,87 @@ document.querySelector("#addWork").addEventListener("submit", e =>
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        document.querySelector("#eduError").classList.remove("hidden");
 | 
			
		||||
        document.querySelector("#eduError div").innerHTML = json.error;
 | 
			
		||||
    }));
 | 
			
		||||
        showErrorMessage(newProjectImage.error, "proj");
 | 
			
		||||
 | 
			
		||||
    })));
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
document.querySelector("#goToCV").addEventListener("click", () =>
 | 
			
		||||
{
 | 
			
		||||
    textareaLoaded = false;
 | 
			
		||||
    document.querySelector("#curriculumVitae").style.display = "block";
 | 
			
		||||
    document.querySelector("#goToCV").classList.add("active");
 | 
			
		||||
    document.querySelector("#projects").style.display = "none";
 | 
			
		||||
    document.querySelector("#goToProjects").classList.remove("active");
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
document.querySelector("#goToProjects").addEventListener("click", () =>
 | 
			
		||||
{
 | 
			
		||||
    textareaLoaded = false;
 | 
			
		||||
    document.querySelector("#curriculumVitae").style.display = "none";
 | 
			
		||||
    document.querySelector("#goToCV").classList.remove("active");
 | 
			
		||||
    document.querySelector("#projects").style.display = "block";
 | 
			
		||||
    document.querySelector("#goToProjects").classList.add("active");
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
document.querySelector("#logout").addEventListener("click", () =>
 | 
			
		||||
{
 | 
			
		||||
    fetch("/api/user/logout").then(res =>
 | 
			
		||||
    {
 | 
			
		||||
        if (res.ok)
 | 
			
		||||
        {
 | 
			
		||||
            window.location.reload();
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
document.querySelector("#eduError .close").addEventListener("click", () =>
 | 
			
		||||
    document.querySelector("#eduError").classList.toggle("hidden"));
 | 
			
		||||
 | 
			
		||||
document.querySelector("#workError .close").addEventListener("click", () =>
 | 
			
		||||
    document.querySelector("#workError").classList.toggle("hidden"));
 | 
			
		||||
 | 
			
		||||
document.querySelector("#projError .close").addEventListener("click", () =>
 | 
			
		||||
    document.querySelector("#projError").classList.toggle("hidden"));
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Shows respective error message for form
 | 
			
		||||
 * @param {string} message The error message to show
 | 
			
		||||
 * @param {string} form The form to show the error message for
 | 
			
		||||
 */
 | 
			
		||||
function showErrorMessage(message, form)
 | 
			
		||||
{
 | 
			
		||||
    document.querySelector(`#${form}Error`).classList.remove("hidden");
 | 
			
		||||
    document.querySelector(`#${form}Error div`).innerText = message;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Switches the timeline item between edit and view mode
 | 
			
		||||
 * @param id the id of the timeline item
 | 
			
		||||
 * @param id the id of the timeline item from the database
 | 
			
		||||
 */
 | 
			
		||||
function edit(id)
 | 
			
		||||
function editCVItem(id)
 | 
			
		||||
{
 | 
			
		||||
    document.querySelector("#timelineItem" + id).classList.toggle("editing");
 | 
			
		||||
    textareaLoaded = false;
 | 
			
		||||
    document.querySelector(`#timelineItem${id}`).classList.toggle("editing");
 | 
			
		||||
    if (id.includes("e"))
 | 
			
		||||
    {
 | 
			
		||||
        document.querySelector("#grade" + id).toggleAttribute("disabled");
 | 
			
		||||
        document.querySelector("#course" + id).toggleAttribute("disabled");
 | 
			
		||||
        document.querySelector(`#grade${id}`).toggleAttribute("disabled");
 | 
			
		||||
        document.querySelector(`#course${id}`).toggleAttribute("disabled");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    document.querySelector("#companyName" + id).toggleAttribute("disabled");
 | 
			
		||||
    document.querySelector("#area" + id).toggleAttribute("disabled");
 | 
			
		||||
    document.querySelector("#jobTitle" + id).toggleAttribute("disabled");
 | 
			
		||||
    document.querySelector(`#companyName${id}`).toggleAttribute("disabled");
 | 
			
		||||
    document.querySelector(`#area${id}`).toggleAttribute("disabled");
 | 
			
		||||
    document.querySelector(`#jobTitle${id}`).toggleAttribute("disabled");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Updates the education timeline item with the given id
 | 
			
		||||
 * @param ID - the id of the course timeline item
 | 
			
		||||
 * @param startPeriod - the start date of the course
 | 
			
		||||
 * @param endPeriod - the end date of the course
 | 
			
		||||
 * @param grade - the grade of the course
 | 
			
		||||
 * @param course - the name of the course
 | 
			
		||||
 * @param prepend - whether to prepend the timeline item to the timeline
 | 
			
		||||
 * @param {number} ID - the id of the course timeline item from the database
 | 
			
		||||
 * @param {string} startPeriod - the start date of the course
 | 
			
		||||
 * @param {string} endPeriod - the end date of the course
 | 
			
		||||
 * @param {string} grade - the grade of the course
 | 
			
		||||
 * @param {string} course - the name of the course
 | 
			
		||||
 * @param {boolean} prepend - whether to prepend the timeline item to the timeline
 | 
			
		||||
 */
 | 
			
		||||
function addEduData(ID, startPeriod, endPeriod, grade, course, prepend=false)
 | 
			
		||||
{
 | 
			
		||||
@ -171,7 +326,7 @@ function addEduData(ID, startPeriod, endPeriod, grade, course, prepend=false)
 | 
			
		||||
    timelineItem.onsubmit = e => updateEduItem(ID, e);
 | 
			
		||||
    timelineItem.innerHTML = `
 | 
			
		||||
    <div class="modifyBtnContainer">
 | 
			
		||||
        <button class="edit" type="button" id="edit${id}" onclick="edit('${id}')"><i class="fa-solid fa-pen-to-square"></i></button>
 | 
			
		||||
        <button class="edit" type="button" id="edit${id}" onclick="editCVItem('${id}')"><i class="fa-solid fa-pen-to-square"></i></button>
 | 
			
		||||
        <button class="delete" type="button" id="delete${id}" onclick="deleteEduItem(${ID})"><i class="fa-solid fa-trash"></i></button>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="dateContainer formControl">
 | 
			
		||||
@ -204,13 +359,13 @@ function addEduData(ID, startPeriod, endPeriod, grade, course, prepend=false)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Adds a new work timeline item to the page
 | 
			
		||||
 * @param ID - the id of the work timeline item
 | 
			
		||||
 * @param startPeriod - the start date of the job
 | 
			
		||||
 * @param endPeriod - the end date of the job
 | 
			
		||||
 * @param companyName - the name of the company
 | 
			
		||||
 * @param area - the area of the company
 | 
			
		||||
 * @param jobTitle - the job title
 | 
			
		||||
 * @param prepend - whether to prepend the timeline item to the timeline
 | 
			
		||||
 * @param {number} ID - the id of the work timeline item from the database
 | 
			
		||||
 * @param {string} startPeriod - the start date of the job
 | 
			
		||||
 * @param {string} endPeriod - the end date of the job
 | 
			
		||||
 * @param {string} companyName - the name of the company
 | 
			
		||||
 * @param {string} area - the area of the company
 | 
			
		||||
 * @param {string} jobTitle - the job title
 | 
			
		||||
 * @param {boolean} prepend - whether to prepend the timeline item to the timeline
 | 
			
		||||
 */
 | 
			
		||||
function addWorkData(ID, startPeriod, endPeriod, companyName, area, jobTitle, prepend=false)
 | 
			
		||||
{
 | 
			
		||||
@ -221,7 +376,7 @@ function addWorkData(ID, startPeriod, endPeriod, companyName, area, jobTitle, pr
 | 
			
		||||
    timelineItem.onsubmit = e => updateWorkItem(ID, e);
 | 
			
		||||
    timelineItem.innerHTML = `
 | 
			
		||||
    <div class="modifyBtnContainer">
 | 
			
		||||
        <button class="edit" type="button" id="edit${id}" onclick="edit('${id}')"><i class="fa-solid fa-pen-to-square"></i></button>
 | 
			
		||||
        <button class="edit" type="button" id="edit${id}" onclick="editCVItem('${id}')"><i class="fa-solid fa-pen-to-square"></i></button>
 | 
			
		||||
        <button class="delete" type="button" id="delete${id}" onclick="deleteWorkItem(${ID})"><i class="fa-solid fa-trash"></i></button>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="dateContainer formControl">
 | 
			
		||||
@ -256,8 +411,9 @@ function addWorkData(ID, startPeriod, endPeriod, companyName, area, jobTitle, pr
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Updates the edu timeline item with the given id
 | 
			
		||||
 * @param id the id of the edu timeline item
 | 
			
		||||
 * @param e the event that triggered the function
 | 
			
		||||
 * and data from the form
 | 
			
		||||
 * @param {number} id the id of the edu timeline item from the database
 | 
			
		||||
 * @param {SubmitEvent} e the event that triggered the function
 | 
			
		||||
 */
 | 
			
		||||
function updateEduItem(id, e)
 | 
			
		||||
{
 | 
			
		||||
@ -303,8 +459,9 @@ function updateEduItem(id, e)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Updates the work timeline item with the given id
 | 
			
		||||
 * @param id the id of the work timeline item
 | 
			
		||||
 * @param e the event that triggered the function
 | 
			
		||||
 * and data from the form
 | 
			
		||||
 * @param {number} id the id of the work timeline item from the database
 | 
			
		||||
 * @param {SubmitEvent} e the event that triggered the function
 | 
			
		||||
 */
 | 
			
		||||
function updateWorkItem(id, e)
 | 
			
		||||
{
 | 
			
		||||
@ -351,7 +508,7 @@ function updateWorkItem(id, e)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Deletes the timeline item with the given id
 | 
			
		||||
 * @param id the id of the timeline item
 | 
			
		||||
 * @param {number} id the id of the timeline item
 | 
			
		||||
 */
 | 
			
		||||
function deleteEduItem(id)
 | 
			
		||||
{
 | 
			
		||||
@ -379,7 +536,7 @@ function deleteEduItem(id)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Updates the timeline item with the given id
 | 
			
		||||
 * @param id the id of the timeline item
 | 
			
		||||
 * @param {number} id the id of the timeline item from the database
 | 
			
		||||
 */
 | 
			
		||||
function deleteWorkItem(id)
 | 
			
		||||
{
 | 
			
		||||
@ -405,3 +562,208 @@ function deleteWorkItem(id)
 | 
			
		||||
 | 
			
		||||
    }); 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Updates the project item with the given id
 | 
			
		||||
 * and data from the form
 | 
			
		||||
 * @param {number} id the id from the database
 | 
			
		||||
 * @param {SubmitEvent} e the event of the form that was submitted
 | 
			
		||||
 */
 | 
			
		||||
function updateProjectItem(id, e)
 | 
			
		||||
{
 | 
			
		||||
    e.preventDefault();
 | 
			
		||||
    let data = {}
 | 
			
		||||
    data["title"] = document.querySelector(`#title${id}`).value;
 | 
			
		||||
    data["isMainProject"] = document.querySelector(`#isMainProject${id}`).checked ? "true" : "false";
 | 
			
		||||
    data["information"] = document.querySelector(`#info${id}`).value;
 | 
			
		||||
    data["projectLink"] = document.querySelector(`#viewProj${id}`).value;
 | 
			
		||||
    data["gitLink"] = document.querySelector(`#git${id}`).value;
 | 
			
		||||
 | 
			
		||||
let imgData = new FormData();
 | 
			
		||||
    imgData.append("img", document.querySelector(`#img${id}`).files[0]);
 | 
			
		||||
 | 
			
		||||
    fetch("/api/projectData/" + id, {
 | 
			
		||||
        method: "PATCH",
 | 
			
		||||
        body: JSON.stringify(data),
 | 
			
		||||
        headers: {
 | 
			
		||||
            "Content-Type": "application/json",
 | 
			
		||||
            "Authorization": "Bearer " + localStorage.getItem("token")
 | 
			
		||||
        }
 | 
			
		||||
    }).then(res =>
 | 
			
		||||
    {
 | 
			
		||||
        if (res.ok)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            if (imgData.get("img") === "undefined")
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
                if (data["isMainProject"] === "true")
 | 
			
		||||
                {
 | 
			
		||||
                    document.querySelectorAll(".isMainProject input").forEach(item => item.checked = false);
 | 
			
		||||
                    document.querySelector(`#isMainProject${id}`).checked = true;
 | 
			
		||||
                    document.querySelector("#projList").prepend(document.querySelector(`#projectItem${id}`));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                document.querySelector(`#projectItem${id}`).classList.toggle("editing");
 | 
			
		||||
                document.querySelector(`#title${id}`).setAttribute("disabled", "");
 | 
			
		||||
                document.querySelector(`#info${id}`).setAttribute("disabled", "");
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            console.log("updating image")
 | 
			
		||||
            return fetch("/api/projectImage/" + id, {
 | 
			
		||||
                method: "POST",
 | 
			
		||||
                body: imgData,
 | 
			
		||||
                headers: {
 | 
			
		||||
                    "Authorization": "Bearer " + localStorage.getItem("token")
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (res.status === 401)
 | 
			
		||||
        {
 | 
			
		||||
            window.location.href = "./";
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        res.json().then(projectDataError =>
 | 
			
		||||
        {
 | 
			
		||||
            document.querySelector(`#projError${id}`).classList.remove("hidden");
 | 
			
		||||
            document.querySelector(`#projError${id} div`).innerHTML = projectDataError.error;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    }).then(res => res.json().then(updatedProjectImage =>
 | 
			
		||||
    {
 | 
			
		||||
        if (res.ok)
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
            if (data["isMainProject"] === "true")
 | 
			
		||||
            {
 | 
			
		||||
                document.querySelectorAll(".isMainProject input").forEach(item => item.checked = false);
 | 
			
		||||
                document.querySelector(`#isMainProject${id}`).checked = true;
 | 
			
		||||
                document.querySelector("#projList").prepend(document.querySelector(`#projectItem${id}`));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            document.querySelector(`#projectItem${id}`).classList.toggle("editing");
 | 
			
		||||
            document.querySelector(`#title${id}`).setAttribute("disabled", "");
 | 
			
		||||
            document.querySelector(`#info${id}`).setAttribute("disabled", "");
 | 
			
		||||
            document.querySelector(`#projectImage${id}`).src = updatedProjectImage.imgLocation;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (res.status === 401)
 | 
			
		||||
        {
 | 
			
		||||
            window.location.href = "./";
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        document.querySelector(`#projError${id}`).classList.remove("hidden");
 | 
			
		||||
        document.querySelector(`#projError${id} div`).innerHTML = projectDataError.error;
 | 
			
		||||
 | 
			
		||||
    }));
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Toggles the editing mode of the project item with the given id
 | 
			
		||||
 * @param id {number} the id of the project item from the database
 | 
			
		||||
 */
 | 
			
		||||
function editProjectItem(id)
 | 
			
		||||
{
 | 
			
		||||
    document.querySelector(`#projectItem${id}`).classList.toggle("editing");
 | 
			
		||||
    document.querySelector(`#title${id}`).removeAttribute("disabled");
 | 
			
		||||
    document.querySelector(`#info${id}`).removeAttribute("disabled");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Deletes the project item with the given id
 | 
			
		||||
 * @param id {number} the id of the project item from the database
 | 
			
		||||
 */
 | 
			
		||||
function deleteProjectItem(id)
 | 
			
		||||
{
 | 
			
		||||
    fetch("/api/projectData/" + id, {
 | 
			
		||||
        method: "DELETE",
 | 
			
		||||
        headers: {
 | 
			
		||||
            "Authorization": "Bearer " + localStorage.getItem("token")
 | 
			
		||||
        }
 | 
			
		||||
    }).then(res =>
 | 
			
		||||
    {
 | 
			
		||||
        if (res.ok)
 | 
			
		||||
        {
 | 
			
		||||
            document.querySelector(`#projectItem${id}`).remove();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (res.status === 401)
 | 
			
		||||
        {
 | 
			
		||||
            window.location.href = "./";
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        res.json().then(json => alert(json.error));
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Adds a new project to the page
 | 
			
		||||
 * @param {number} id the id of the project from the database
 | 
			
		||||
 * @param {string} isMainProject is it the main project
 | 
			
		||||
 * @param {string} imgLocation the relative path of the image
 | 
			
		||||
 * @param {string} title the title of the project
 | 
			
		||||
 * @param {string} information the information about the project
 | 
			
		||||
 * @param {string} projectLink the link to the project
 | 
			
		||||
 * @param {string} gitLink the link to the git repository
 | 
			
		||||
 */
 | 
			
		||||
function addProject(id , isMainProject, imgLocation, title, information, projectLink, gitLink)
 | 
			
		||||
{
 | 
			
		||||
    let projectItem = document.createElement("form");
 | 
			
		||||
    projectItem.id = "projectItem" + id;
 | 
			
		||||
    projectItem.classList.add("projItem");
 | 
			
		||||
    projectItem.onsubmit = e => updateProjectItem(id, e);
 | 
			
		||||
    projectItem.innerHTML = `
 | 
			
		||||
        <div class="modifyBtnContainer">
 | 
			
		||||
            <button class="edit" type="button" id="edit${id}" onclick="editProjectItem(${id})"><i class="fa-solid fa-pen-to-square"></i></button>
 | 
			
		||||
            <button class="delete" type="button" id="delete${id}" onclick="deleteProjectItem(${id})"><i class="fa-solid fa-trash"></i></button>
 | 
			
		||||
        </div>
 | 
			
		||||
        <img class="displayedImage" id="projectImage${id}" src="${imgLocation}" alt="image preivew of the project">
 | 
			
		||||
        <div class="formControl imageContainer">
 | 
			
		||||
            <input type="file" name="img${id}" id="img${id}">
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="formControl projectTitleContainer">
 | 
			
		||||
            <input type="text" name="title${id}" id="title${id}" value="${title}" disabled>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="formControl isMainProject">
 | 
			
		||||
            <label class="checkContainer" for="isMainProject${id}">Is It The Main Project
 | 
			
		||||
                <input type="checkbox" id="isMainProject${id}" name="isMainProject${id}" ${(isMainProject === "true" ? "checked" : "")}>
 | 
			
		||||
                <span class="checkmark"></span>
 | 
			
		||||
            </label>
 | 
			
		||||
        </div> 
 | 
			
		||||
        <div class="formControl infoContainer">
 | 
			
		||||
            <textarea name="info${id}" id="info${id}" disabled>${information}</textarea>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="formControl viewProjContainer">
 | 
			
		||||
            <input type="text" name="viewProj${id}" id="viewProj${id}" value="${projectLink}">
 | 
			
		||||
        </div>    
 | 
			
		||||
        <div class="formControl gitContainer">
 | 
			
		||||
            <input type="text" name="git${id}" id="git${id}" value="${gitLink}">
 | 
			
		||||
        </div>    
 | 
			
		||||
        <div class="error hidden" id="projError${id}">
 | 
			
		||||
            <button class="close" type="button" onclick="this.parentElement.classList.toggle('hidden');">×</button>
 | 
			
		||||
            <div></div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <input type="submit" value="Change">
 | 
			
		||||
        <div class="linkContainer">
 | 
			
		||||
           <a href="${(projectLink === "N/A") ? "#" : projectLink}" class="btn btnPrimary boxShadowIn boxShadowOut"${(projectLink === "N/A") ? "disabled=\"disabled\"" : ""}>View Project</a>
 | 
			
		||||
           <a href="${(gitLink === "N/A") ? "#" : gitLink}" class="btn btnOutline boxShadowIn boxShadowOut">${(gitLink === "N/A") ? "disabled=\"disabled\"" : ""}Git</a>
 | 
			
		||||
        </div>
 | 
			
		||||
    `;
 | 
			
		||||
 | 
			
		||||
    if (isMainProject === "true")
 | 
			
		||||
    {
 | 
			
		||||
        document.querySelectorAll(".isMainProject input").forEach(item => item.checked = false);
 | 
			
		||||
 | 
			
		||||
        document.querySelector("#projList").prepend(projectItem);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    document.querySelector("#projList").appendChild(projectItem);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -11,12 +11,23 @@ document.addEventListener("DOMContentLoaded", _ =>
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Shows respective error message for form
 | 
			
		||||
 * @param {string} message The error message to show
 | 
			
		||||
 * @param {string} form The form to show the error message for
 | 
			
		||||
 */
 | 
			
		||||
function showErrorMessage(message, form) 
 | 
			
		||||
{
 | 
			
		||||
    document.querySelector(`#${form}Error`).classList.remove("hidden");
 | 
			
		||||
    document.querySelector(`#${form}Error div`).innerText = message;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Switches between the different forms
 | 
			
		||||
 * @param {string} from The css selector of form to switch from
 | 
			
		||||
 * @param {string} to The css selector of form to switch to
 | 
			
		||||
 */
 | 
			
		||||
function switchView(from, to)
 | 
			
		||||
{
 | 
			
		||||
    document.querySelector(from).classList.toggle("shown");
 | 
			
		||||
 | 
			
		||||
@ -171,12 +171,12 @@ function getProjectData()
 | 
			
		||||
						document.getElementById("mainProj").innerHTML = `
 | 
			
		||||
						<h1>${item["title"]}</h1>
 | 
			
		||||
						<div>
 | 
			
		||||
							<img src="imgs/1000x800.jpg" alt="">
 | 
			
		||||
							<img src="${(item["imgLocation"] === "") ?  "../imgs/placeholder.png" : item["imgLocation"]}" alt="">
 | 
			
		||||
							<div class="flexRow">
 | 
			
		||||
								<p>${item["information"]}</p>
 | 
			
		||||
								<div class="flexCol">
 | 
			
		||||
									<a href="${(item["projectLink"] === "N/A") ? "#" : item["projectLink"]}" class="btn btnPrimary boxShadowIn boxShadowOut" ${(item["projectLink"] === "N/A") ? "disabled=\"disabled\"" : ""}>View Project</a>
 | 
			
		||||
									<a href="${(item["githubLink"] === "N/A") ? "#" : item["gitubLink"]}" class="btn btnOutline boxShadowIn boxShadowOut" ${(item["githubLink"] === "N/A") ? "disabled=\"disabled\"" : ""}>GitHub</a>
 | 
			
		||||
									<a href="${(item["gitLink"] === "N/A") ? "#" : item["gitLink"]}" class="btn btnOutline boxShadowIn boxShadowOut" ${(item["gitLink"] === "N/A") ? "disabled=\"disabled\"" : ""}>Git</a>
 | 
			
		||||
								</div>
 | 
			
		||||
							</div>
 | 
			
		||||
						</div>
 | 
			
		||||
@ -186,14 +186,15 @@ function getProjectData()
 | 
			
		||||
 | 
			
		||||
                    document.querySelector("#otherProj div").innerHTML += `
 | 
			
		||||
                    <div class="oProjItem">
 | 
			
		||||
                        <img src="imgs/500x400.jpg" alt="">
 | 
			
		||||
                        <img src="${(item["imgLocation"] === "") ?  "../imgs/placeholder.png" : item["imgLocation"]}" alt="">
 | 
			
		||||
                        <div class="flexCol">
 | 
			
		||||
                            <div>
 | 
			
		||||
                            	<h3>${item["title"]}</h3>
 | 
			
		||||
                                <p>${item["information"]}</p>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div>
 | 
			
		||||
                                <a href="${(item["projectLink"] === "N/A") ? "#" : item["projectLink"]}" class="btn btnPrimary boxShadowIn boxShadowOut"${(item["projectLink"] === "N/A") ? "disabled=\"disabled\"" : ""}>View Project</a>
 | 
			
		||||
                                <a href="${(item["githubLink"] === "N/A") ? "#" : item["gitubLink"]}" class="btn btnOutline boxShadowIn boxShadowOut">${(item["githubLink"] === "N/A") ? "disabled=\"disabled\"" : ""}Github</a>
 | 
			
		||||
                                <a href="${(item["githubLink"] === "N/A") ? "#" : item["gitubLink"]}" class="btn btnOutline boxShadowIn boxShadowOut">${(item["githubLink"] === "N/A") ? "disabled=\"disabled\"" : ""}Git</a>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user