Initial commit

This commit is contained in:
Madeorsk 2018-08-12 14:30:38 +02:00
parent 89b496b23c
commit cbb85097e3
102 changed files with 98513 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
## IDE ##
.idea/
## Composer ##
vendor/
composer.lock
## App ##
db/

18
composer.json Normal file
View File

@ -0,0 +1,18 @@
{
"name": "madeorsk/poll-verlaine",
"type": "project",
"require": {
"mikecao/flight": "^1.3"
},
"license": "AGPL-3.0",
"authors": [
{
"name": "Madeorsk",
"email": "madeorsk@protonmail.com"
},
{
"name": "Tagada",
"email": "madeorsk@protonmail.com"
}
]
}

50
index.php Normal file
View File

@ -0,0 +1,50 @@
<?php
require __DIR__ . "/vendor/autoload.php";
require __DIR__ . "/models/Poll.php";
function format_poll($poll)
{
return [
"id" => $poll->id,
"title" => $poll->title,
"creation_date" => $poll->creation_date,
"options" => $poll->options,
];
}
Flight::route("POST /polls", function () {
$request = Flight::request();
if ($request->type === "application/json")
{
$request_json = $request->data;
$poll = Poll::create_poll($request_json);
if ($poll)
Flight::json(format_poll($poll));
else
Flight::halt(403, "<h1>403 Forbidden</h1><h3>Invalid data.</h3>");
}
else
Flight::halt(403, "<h1>403 Forbidden</h1><h3>Invalid Content-Type.</h3>");
});
Flight::route("GET /polls/@id:[a-fA-F0-9]+", function ($id) {
$poll = Poll::load_poll($id);
if ($poll)
{
if (Flight::request()->type === "application/json")
Flight::json(format_poll($poll));
else
{
Flight::render("poll", ["poll" => $poll], "body_content");
Flight::render("layout");
}
}
else
Flight::notFound();
});
Flight::route("/", function () {
Flight::render("home", [], "body_content");
Flight::render("layout");
});
Flight::start();

94
models/Poll.php Normal file
View File

@ -0,0 +1,94 @@
<?php
define("SAVE_PATH", __DIR__ . "/../db");
class Poll
{
/**
* Create a new poll and save it.
*/
public static function create_poll($request_data)
{
try
{
$poll = new Poll();
$poll->title = $request_data->title;
$poll->creation_date = (new DateTime())->getTimestamp();
$id = 0;
foreach ($request_data->options as $option)
{
$poll->options[] = [
"id" => $id,
"label" => $option,
"votes" => 0,
];
$id++;
}
$poll->gen_new_id();
$poll->save();
return $poll;
}
catch (Exception $e)
{ return false; }
}
/**
* Try to load an existing poll.
* @param string id - Poll ID.
* @return boolean|Poll - Requested poll if found, false otherwise.
*/
public static function load_poll($id)
{
$db = dba_open(SAVE_PATH . "/polls.db", "rd");
if (dba_exists($id, $db))
{
$poll = new Poll();
$saved_poll_data = json_decode(dba_fetch($id, $db));
$poll->id = $id;
$poll->title = $saved_poll_data->title;
$poll->creation_date = $saved_poll_data->creation_date;
$poll->options = $saved_poll_data->options;
dba_close($db);
return $poll;
}
else
{
dba_close($db);
return false;
}
}
public $id = null;
public $title;
public $creation_date;
public $options = [];
public function gen_new_id()
{
$db = dba_open(SAVE_PATH . "/polls.db", "rd");
function gen_id()
{ return bin2hex(openssl_random_pseudo_bytes(16)); }
do
{ $new_id = gen_id(); }
while(dba_exists($new_id, $db));
dba_close($db);
$this->id = $new_id;
}
public function save()
{
$db = dba_open(SAVE_PATH . "/polls.db", "wd");
$func = (dba_exists($this->id, $db) ? "dba_replace" : "dba_insert");
$func($this->id, json_encode([
"title" => $this->title,
"creation_date" => $this->creation_date,
"options" => $this->options,
]), $db);
dba_close($db);
}
}

98
static/css/main.css Normal file
View File

@ -0,0 +1,98 @@
@import url("/static/fonts/Nunito/Nunito.css");
@import url("/static/fonts/PTSerif/PTSerif.css");
html, body
{
margin: 0;
padding: 0;
}
body
{
background: #242424;
color: #ECECEC;
font-family: "Nunito", sans-serif;
}
::-moz-focus-inner
{ border: none; }
body h1
{
display: block;
margin: 1em auto;
font-size: 4em;
font-weight: 300;
text-align: center;
}
main
{
margin: 0 5%;
}
main form input,
main form button
{
transition: background 0.1s ease-in;
display: block;
margin: auto;
padding: 1em;
width: 25rem;
box-sizing: border-box;
background: #141414;
border: none;
outline: none;
font-size: 1.3em;
text-align: center;
}
main form input[type="submit"],
main form button
{ cursor: pointer; }
main form input[type="submit"]
{ margin-top: 1em; }
main form input:focus,
main form input[type="submit"]:hover,
main form button:hover
{ background: #1D1D1D; }
main form input[name="title"],
main form input[name="title"]:focus
{
background: transparent;
font-family: "PT Serif", serif;
font-size: 1.5em;
}
main #choices .choice
{
display: flex;
flex-direction: row;
margin: auto;
width: 25rem;
}
main #choices .choice > *
{ margin: 0; }
main #choices .choice input
{ font-family: "PT Serif", serif; }
main #choices .choice .delete
{
background: #FF2E31;
width: 4em;
}
footer
{
display: block;
margin: 5em;
color: #8E8E8E;
font-size: 0.9em;
font-weight: 700;
text-align: center;
}

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 531 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 541 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 532 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 539 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 516 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 526 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 518 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 521 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 534 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 518 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 526 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 526 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 528 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 535 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,168 @@
@font-face {
font-family: 'Nunito';
src: url('Nunito-ExtraBoldItalic.eot');
src: url('Nunito-ExtraBoldItalic.eot?#iefix') format('embedded-opentype'),
url('Nunito-ExtraBoldItalic.woff2') format('woff2'),
url('Nunito-ExtraBoldItalic.woff') format('woff'),
url('Nunito-ExtraBoldItalic.ttf') format('truetype'),
url('Nunito-ExtraBoldItalic.svg#Nunito-ExtraBoldItalic') format('svg');
font-weight: 800;
font-style: italic;
}
@font-face {
font-family: 'Nunito';
src: url('Nunito-ExtraLightItalic.eot');
src: url('Nunito-ExtraLightItalic.eot?#iefix') format('embedded-opentype'),
url('Nunito-ExtraLightItalic.woff2') format('woff2'),
url('Nunito-ExtraLightItalic.woff') format('woff'),
url('Nunito-ExtraLightItalic.ttf') format('truetype'),
url('Nunito-ExtraLightItalic.svg#Nunito-ExtraLightItalic') format('svg');
font-weight: 200;
font-style: italic;
}
@font-face {
font-family: 'Nunito';
src: url('Nunito-ExtraBold.eot');
src: url('Nunito-ExtraBold.eot?#iefix') format('embedded-opentype'),
url('Nunito-ExtraBold.woff2') format('woff2'),
url('Nunito-ExtraBold.woff') format('woff'),
url('Nunito-ExtraBold.ttf') format('truetype'),
url('Nunito-ExtraBold.svg#Nunito-ExtraBold') format('svg');
font-weight: 800;
font-style: normal;
}
@font-face {
font-family: 'Nunito';
src: url('Nunito-Regular.eot');
src: url('Nunito-Regular.eot?#iefix') format('embedded-opentype'),
url('Nunito-Regular.woff2') format('woff2'),
url('Nunito-Regular.woff') format('woff'),
url('Nunito-Regular.ttf') format('truetype'),
url('Nunito-Regular.svg#Nunito-Regular') format('svg');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Nunito';
src: url('Nunito-BlackItalic.eot');
src: url('Nunito-BlackItalic.eot?#iefix') format('embedded-opentype'),
url('Nunito-BlackItalic.woff2') format('woff2'),
url('Nunito-BlackItalic.woff') format('woff'),
url('Nunito-BlackItalic.ttf') format('truetype'),
url('Nunito-BlackItalic.svg#Nunito-BlackItalic') format('svg');
font-weight: 900;
font-style: italic;
}
@font-face {
font-family: 'Nunito';
src: url('Nunito-Italic.eot');
src: url('Nunito-Italic.eot?#iefix') format('embedded-opentype'),
url('Nunito-Italic.woff2') format('woff2'),
url('Nunito-Italic.woff') format('woff'),
url('Nunito-Italic.ttf') format('truetype'),
url('Nunito-Italic.svg#Nunito-Italic') format('svg');
font-weight: normal;
font-style: italic;
}
@font-face {
font-family: 'Nunito';
src: url('Nunito-BoldItalic.eot');
src: url('Nunito-BoldItalic.eot?#iefix') format('embedded-opentype'),
url('Nunito-BoldItalic.woff2') format('woff2'),
url('Nunito-BoldItalic.woff') format('woff'),
url('Nunito-BoldItalic.ttf') format('truetype'),
url('Nunito-BoldItalic.svg#Nunito-BoldItalic') format('svg');
font-weight: bold;
font-style: italic;
}
@font-face {
font-family: 'Nunito';
src: url('Nunito-LightItalic.eot');
src: url('Nunito-LightItalic.eot?#iefix') format('embedded-opentype'),
url('Nunito-LightItalic.woff2') format('woff2'),
url('Nunito-LightItalic.woff') format('woff'),
url('Nunito-LightItalic.ttf') format('truetype'),
url('Nunito-LightItalic.svg#Nunito-LightItalic') format('svg');
font-weight: 300;
font-style: italic;
}
@font-face {
font-family: 'Nunito';
src: url('Nunito-SemiBoldItalic.eot');
src: url('Nunito-SemiBoldItalic.eot?#iefix') format('embedded-opentype'),
url('Nunito-SemiBoldItalic.woff2') format('woff2'),
url('Nunito-SemiBoldItalic.woff') format('woff'),
url('Nunito-SemiBoldItalic.ttf') format('truetype'),
url('Nunito-SemiBoldItalic.svg#Nunito-SemiBoldItalic') format('svg');
font-weight: 600;
font-style: italic;
}
@font-face {
font-family: 'Nunito';
src: url('Nunito-Bold.eot');
src: url('Nunito-Bold.eot?#iefix') format('embedded-opentype'),
url('Nunito-Bold.woff2') format('woff2'),
url('Nunito-Bold.woff') format('woff'),
url('Nunito-Bold.ttf') format('truetype'),
url('Nunito-Bold.svg#Nunito-Bold') format('svg');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: 'Nunito';
src: url('Nunito-ExtraLight.eot');
src: url('Nunito-ExtraLight.eot?#iefix') format('embedded-opentype'),
url('Nunito-ExtraLight.woff2') format('woff2'),
url('Nunito-ExtraLight.woff') format('woff'),
url('Nunito-ExtraLight.ttf') format('truetype'),
url('Nunito-ExtraLight.svg#Nunito-ExtraLight') format('svg');
font-weight: 200;
font-style: normal;
}
@font-face {
font-family: 'Nunito';
src: url('Nunito-SemiBold.eot');
src: url('Nunito-SemiBold.eot?#iefix') format('embedded-opentype'),
url('Nunito-SemiBold.woff2') format('woff2'),
url('Nunito-SemiBold.woff') format('woff'),
url('Nunito-SemiBold.ttf') format('truetype'),
url('Nunito-SemiBold.svg#Nunito-SemiBold') format('svg');
font-weight: 600;
font-style: normal;
}
@font-face {
font-family: 'Nunito';
src: url('Nunito-Black.eot');
src: url('Nunito-Black.eot?#iefix') format('embedded-opentype'),
url('Nunito-Black.woff2') format('woff2'),
url('Nunito-Black.woff') format('woff'),
url('Nunito-Black.ttf') format('truetype'),
url('Nunito-Black.svg#Nunito-Black') format('svg');
font-weight: 900;
font-style: normal;
}
@font-face {
font-family: 'Nunito';
src: url('Nunito-Light.eot');
src: url('Nunito-Light.eot?#iefix') format('embedded-opentype'),
url('Nunito-Light.woff2') format('woff2'),
url('Nunito-Light.woff') format('woff'),
url('Nunito-Light.ttf') format('truetype'),
url('Nunito-Light.svg#Nunito-Light') format('svg');
font-weight: 300;
font-style: normal;
}

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 433 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 469 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 424 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,48 @@
@font-face {
font-family: 'PT Serif';
src: url('PTSerif-BoldItalic.eot');
src: url('PTSerif-BoldItalic.eot?#iefix') format('embedded-opentype'),
url('PTSerif-BoldItalic.woff2') format('woff2'),
url('PTSerif-BoldItalic.woff') format('woff'),
url('PTSerif-BoldItalic.ttf') format('truetype'),
url('PTSerif-BoldItalic.svg#PTSerif-BoldItalic') format('svg');
font-weight: bold;
font-style: italic;
}
@font-face {
font-family: 'PT Serif';
src: url('PTSerif-Bold.eot');
src: url('PTSerif-Bold.eot?#iefix') format('embedded-opentype'),
url('PTSerif-Bold.woff2') format('woff2'),
url('PTSerif-Bold.woff') format('woff'),
url('PTSerif-Bold.ttf') format('truetype'),
url('PTSerif-Bold.svg#PTSerif-Bold') format('svg');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: 'PT Serif';
src: url('PTSerif-Italic.eot');
src: url('PTSerif-Italic.eot?#iefix') format('embedded-opentype'),
url('PTSerif-Italic.woff2') format('woff2'),
url('PTSerif-Italic.woff') format('woff'),
url('PTSerif-Italic.ttf') format('truetype'),
url('PTSerif-Italic.svg#PTSerif-Italic') format('svg');
font-weight: normal;
font-style: italic;
}
@font-face {
font-family: 'PT Serif';
src: url('PTSerif-Regular.eot');
src: url('PTSerif-Regular.eot?#iefix') format('embedded-opentype'),
url('PTSerif-Regular.woff2') format('woff2'),
url('PTSerif-Regular.woff') format('woff'),
url('PTSerif-Regular.ttf') format('truetype'),
url('PTSerif-Regular.svg#PTSerif-Regular') format('svg');
font-weight: normal;
font-style: normal;
}

1
static/js/fetch.min.js vendored Normal file

File diff suppressed because one or more lines are too long

50
static/js/new.js Normal file
View File

@ -0,0 +1,50 @@
let next_id = 1;
document.addEventListener("DOMContentLoaded", () => {
let choices = document.getElementById("choices");
let choice_template = document.getElementById("choice");
function create_choice()
{
let choice = document.createElement("div");
choice.classList.add("choice");
choice.innerHTML = choice_template.innerHTML.replace(/:id/g, next_id);
choice.querySelector("button.delete").addEventListener("click", () => {
choices.removeChild(choice);
});
choices.append(choice);
next_id++;
}
document.getElementById("add-choice").addEventListener("click", create_choice);
while(next_id < 4) create_choice();
let form = document.getElementById("newpoll");
form.addEventListener("submit", (event) => {
event.preventDefault();
function get_choices(form)
{
let choices = [];
form.querySelectorAll("#choices .choice input").forEach((el) => {
choices.push(el.value);
});
return choices;
}
fetch("/polls", {
method: "POST",
body: JSON.stringify({
title: form.querySelector(`input[name="title"]`).value,
options: get_choices(form),
}),
headers: {
"Content-Type": "application/json",
},
}).then((res) => {
return res.json();
}).then((json) => {
console.log(json);
});
});
});

21
views/home.php Normal file
View File

@ -0,0 +1,21 @@
<body class="home">
<script src="/static/js/fetch.min.js"></script>
<script src="/static/js/new.js"></script>
<h1>Poll Verlaine</h1>
<main>
<form action="#" id="newpoll">
<input type="text" name="title" placeholder="What do you want to ask?" />
<div id="choices">
</div>
<button type="button" id="add-choice">New choice</button>
<input type="submit" value="Create poll" />
</form>
</main>
<template id="choice">
<input type="text" id="choice-:id" placeholder="Another choice" />
<button type="button" class="delete" tabindex="-1" title="Delete" aria-label="Delete"></button>
</template>
</body>

Some files were not shown because too many files have changed in this diff Show More