• R/O
  • SSH
  • HTTPS

pworg: Commit


Commit MetaInfo

Revision3 (tree)
Time2021-09-30 22:07:38
Authormateuszviste

Log Message

add initial files

Change Summary

Incremental Difference

--- trunk/index.php (nonexistent)
+++ trunk/index.php (revision 3)
@@ -0,0 +1,314 @@
1+<!DOCTYPE html>
2+<html>
3+<head>
4+ <title>pworg</title>
5+ <link rel="stylesheet" href="style.css">
6+ <meta charset="utf-8">
7+ <meta name="viewport" content="width=device-width, initial-scale=1">
8+</head>
9+<?php
10+
11+/****************************************************************************
12+ * pworg - personal web organizer http://pworg.osdn.io *
13+ * Copyright (C) Mateusz Viste MIT License *
14+ ****************************************************************************/
15+
16+
17+// type can be 'TODO', 'EVENT' or 'MEMO'
18+function sql_load_item_by_id($type, $id) {
19+ $db = new SQLite3('agenda.sqlite3', SQLITE3_OPEN_READONLY);
20+ if (!$db) return(FALSE);
21+
22+ if ($type === 'TODO') {
23+ $stmt = $db->prepare('SELECT * FROM todos WHERE id = :id');
24+ } else if ($type === 'EVENT') {
25+ $stmt = $db->prepare('SELECT * FROM events WHERE id = :id');
26+ } else if ($type === 'MEMO') {
27+ $stmt = $db->prepare('SELECT * FROM memos WHERE id = :id');
28+ }
29+ $stmt->bindvalue(':id', $id, SQLITE3_INTEGER);
30+ $sqlres = $stmt->execute();
31+
32+ $result = $sqlres->fetcharray();
33+ $sqlres->finalize();
34+ $db->close();
35+
36+ return($result);
37+}
38+
39+
40+function day2formdate($day) {
41+ return(gmdate('Y-m-d', $day * 86400));
42+}
43+
44+
45+function formdate2day($d) {
46+ return(intval(strtotime($d) / 86400));
47+}
48+
49+
50+// converts integer into a time in the 24h format (like 23:30)
51+function time2formtime($t) {
52+ return(sprintf('%02u:%02u', $t >> 8, $t & 0xff));
53+}
54+
55+
56+function formtime2time($t) {
57+ $arr = explode(':', $t, 2);
58+ $h = intval($arr[0]);
59+ $m = intval($arr[1]);
60+ return(($h << 8) | $m);
61+}
62+
63+
64+function formularz($id, $day, $formtype) {
65+ // preload data if it is about editing
66+ if ($id >= 0) {
67+ $obj = sql_load_item_by_id($formtype, $id);
68+ }
69+
70+ echo '<div class="column">' . "\n";
71+ echo '<div class="columntitle">' . $formtype . '</div><br>' . "\n";
72+ echo '<form method="post" action="?day=' . $day . '">' . "\n";
73+ echo '<input type="hidden" name="formtype" value="' . $formtype . '">' . "\n";
74+ echo '<input type="hidden" name="id" value="' . $id . '">' . "\n";
75+
76+ if (($formtype === 'TODO') || ($formtype === 'MEMO')) {
77+ echo "notbefore:<br>\n";
78+ if (!empty($obj)) {
79+ $val = $obj['notbefore'];
80+ } else {
81+ $val = $day;
82+ }
83+ echo '<input required type="date" name="notbefore" value="' . day2formdate($val) . '">' . "<br>\n";
84+ }
85+
86+ if ($formtype === 'MEMO') {
87+ echo "notafter:<br>\n";
88+ if (!empty($obj)) {
89+ $val = $obj['notafter'];
90+ } else {
91+ $val = $day;
92+ }
93+ echo '<input required type="date" name="notafter" value="' . day2formdate($val) . '">' . "<br>\n";
94+ }
95+
96+ if ($formtype === 'EVENT') {
97+ echo "day:<br>\n";
98+ if (!empty($obj)) {
99+ $val = $obj['day'];
100+ } else {
101+ $val = $day;
102+ }
103+ echo '<input required type="date" name="d" value="' . day2formdate($val) . '">' . "<br>\n";
104+
105+ echo "time range:<br>\n";
106+ echo '<div class="timerange">' . "\n";
107+ if (!empty($obj)) {
108+ $val1 = $obj['timefrom'];
109+ $val2 = $obj['timeto'];
110+ } else {
111+ $val1 = '';
112+ $val2 = '';
113+ }
114+ echo '<input required type="time" name="timefrom" value="' . time2formtime($val1) . '"> --> <input required type="time" name="timeto" value="' . time2formtime($val2) . '">' . "\n";
115+ echo "</div>\n";
116+ }
117+
118+ echo "title:<br>\n";
119+ if (!empty($obj)) {
120+ $val = $obj['title'];
121+ } else {
122+ $val = '';
123+ }
124+ echo '<input required autofocus name="title" type="text" value="' . htmlspecialchars($val) . '">' . "<br>\n";
125+
126+ echo "description:<br>\n";
127+ if (!empty($obj)) {
128+ $val = $obj['description'];
129+ } else {
130+ $val = '';
131+ }
132+ echo '<textarea name="desc">' . htmlspecialchars($obj['description']) . "</textarea><br>\n";
133+
134+ echo '<div class="btns"><a href="./?day=' . $day . '">cancel</a> <input type="submit" value="submit"></div>' . "\n";
135+ echo "</form>\n";
136+ echo "</div>\n";
137+}
138+
139+
140+function get_or_post($var, $fallback) {
141+ $r = $fallback;
142+ if (!empty($_GET[$var])) $r = $_GET[$var];
143+ if (!empty($_POST[$var])) $r = $_POST[$var];
144+ return($r);
145+}
146+
147+
148+setlocale(LC_ALL, 'pl_PL.utf8');
149+date_default_timezone_set('UTC');
150+
151+$today = intval(time() / 86400);
152+
153+$a = get_or_post('a', '');
154+$day = get_or_post('day', $today);
155+$id = get_or_post('id', -1);
156+$formtype = get_or_post('formtype', '');
157+
158+if (!empty($_GET['jumpto'])) {
159+ $day = intval(strtotime($_GET['jumpto']) / 86400);
160+}
161+
162+if ($a === 'event') {
163+ formularz($id, $day, 'EVENT');
164+ goto GAMEOVER;
165+}
166+if ($a === 'memo') {
167+ formularz($id, $day, 'MEMO');
168+ goto GAMEOVER;
169+}
170+if ($a === 'todo') {
171+ formularz($id, $day, 'TODO');
172+ goto GAMEOVER;
173+}
174+
175+if ($a === 'jmp') {
176+ echo '<div class="column">' . "\n";
177+ echo ' <div class="columntitle">GO TO DATE</div><br>' . "\n";
178+ echo ' <form action="./">' . "\n";
179+ echo ' <input required type="date" name="jumpto" value="' . day2formdate($today) . '">' . "\n";
180+ echo ' <input type="submit" value="jump">' . "\n";
181+ echo " </form>\n";
182+ echo "</div>\n";
183+ goto GAMEOVER;
184+}
185+
186+
187+// is there anything to save into db?
188+if (!empty($formtype)) {
189+ $db = new SQLite3('agenda.sqlite3');
190+
191+ if ($formtype === 'EVENT') {
192+ echo "<!-- ";
193+ var_dump($_POST);
194+ echo " -->";
195+ if ($id < 0) {
196+ $stmt = $db->prepare('INSERT INTO events (day, timefrom, timeto, title, description) VALUES (:day, :tf, :tt, :tit, :desc)');
197+ } else {
198+ $stmt = $db->prepare('REPLACE INTO events (day, timefrom, timeto, title, description, id) VALUES (:day, :tf, :tt, :tit, :desc, :id)');
199+ $stmt->bindvalue(':id', $id, SQLITE3_INTEGER);
200+ }
201+ $stmt->bindvalue(':day', formdate2day($_POST['d']), SQLITE3_INTEGER);
202+ $stmt->bindvalue(':tf', formtime2time($_POST['timefrom']), SQLITE3_INTEGER);
203+ $stmt->bindvalue(':tt', formtime2time($_POST['timeto']), SQLITE3_INTEGER);
204+ $stmt->bindvalue(':tit', $_POST['title'], SQLITE3_TEXT);
205+ $stmt->bindvalue(':desc', $_POST['desc'], SQLITE3_TEXT);
206+ $stmt->execute();
207+ } else if ($formtype === 'MEMO') {
208+ if ($id < 0) {
209+ $stmt = $db->prepare('INSERT INTO memos (notbefore, notafter, title, description) VALUES (:nb, :na, :tit, :desc)');
210+ } else {
211+ $stmt = $db->prepare('REPLACE INTO memos (notbefore, notafter, title, description, id) VALUES (:nb, :na, :tit, :desc, :id)');
212+ $stmt->bindvalue(':id', $id, SQLITE3_INTEGER);
213+ }
214+ $stmt->bindvalue(':nb', formdate2day($_POST['notbefore']), SQLITE3_INTEGER);
215+ $stmt->bindvalue(':na', formdate2day($_POST['notafter']), SQLITE3_INTEGER);
216+ $stmt->bindvalue(':tit', $_POST['title'], SQLITE3_TEXT);
217+ $stmt->bindvalue(':desc', $_POST['desc'], SQLITE3_TEXT);
218+ $stmt->execute();
219+ } else if ($formtype === 'TODO') {
220+ if ($id < 0) {
221+ $stmt = $db->prepare('INSERT INTO todos (notbefore, title, description, doneday) VALUES (:nb, :tit, :desc, 0)');
222+ } else {
223+ $stmt = $db->prepare('REPLACE INTO todos (notbefore, title, description, doneday, id) VALUES (:nb, :tit, :desc, 0, :id)');
224+ $stmt->bindvalue(':id', $id, SQLITE3_INTEGER);
225+ }
226+ $stmt->bindvalue(':nb', formdate2day($_POST['notbefore']), SQLITE3_INTEGER);
227+ $stmt->bindvalue(':tit', $_POST['title'], SQLITE3_TEXT);
228+ $stmt->bindvalue(':desc', $_POST['desc'], SQLITE3_TEXT);
229+ $stmt->execute();
230+ }
231+ $db->close;
232+}
233+
234+// open db
235+$db = new SQLite3('agenda.sqlite3', SQLITE3_OPEN_READONLY);
236+if (! $db) {
237+ // open sql db and create schema if needed
238+ $db = new SQLite3('agenda.sqlite3');
239+ if ($db) {
240+ $db->exec('CREATE TABLE IF NOT EXISTS memos (id INTEGER PRIMARY KEY, notbefore INTEGER NOT NULL, notafter INTEGER NOT NULL, title TEXT NOT NULL, description TEXT NULL);');
241+ $db->exec('CREATE INDEX IF NOT EXISTS memos_idx ON memos (notbefore, notafter);');
242+ $db->exec('CREATE TABLE IF NOT EXISTS events (id INTEGER PRIMARY KEY, day INTEGER NOT NULL, timefrom INTEGER NOT NULL, timeto INTEGER NOT NULL, title TEXT NOT NULL, description TEXT NULL);');
243+ $db->exec('CREATE INDEX IF NOT EXISTS events_idx ON events (day);');
244+ $db->exec('CREATE TABLE IF NOT EXISTS todos (id INTEGER PRIMARY KEY, notbefore INTEGER NOT NULL, title TEXT NOT NULL, description TEXT NULL, doneday INTEGER NOT NULL);');
245+ $db->exec('CREATE INDEX IF NOT EXISTS todos_idx ON todos (notbefore, doneday);');
246+ $db->close();
247+ }
248+ // try again now
249+ $db = new SQLite3('agenda.sqlite3', SQLITE3_OPEN_READONLY);
250+ if (! $db) {
251+ echo '<p>ERROR: DATABASE ACCESS FAILED</p>';
252+ goto GAMEOVER;
253+ }
254+}
255+
256+echo '<div class="column">' . "\n";
257+echo ' <div class="columntitle">';
258+echo '<a href="?day=' . ($day - 1) . '">&lt;&lt;</a>';
259+echo '<div class="datefield"><a href="?a=jmp">' . strftime('%d %b %Y<br>(%A)', $day * 86400) . '</a></div>';
260+echo '<a href="?day=' . ($day + 1) . '">&gt;&gt;</a>' . "</div>\n";
261+
262+// display memos
263+$sqlres = $db->query("SELECT * FROM memos WHERE notbefore <= {$day} AND notafter >= {$day} ORDER BY title;");
264+while ($row = $sqlres->fetcharray()) {
265+ echo ' <a href="?a=memo&amp;day=' . $day . '&amp;id=' . $row['id'] . '"><div class="memo">' . htmlspecialchars($row['title']) . "</div></a>\n";
266+}
267+$sqlres->finalize();
268+
269+// display events
270+$sqlres = $db->query("SELECT * FROM events WHERE day = {$day} ORDER BY timefrom;");
271+$eventsfound = 0;
272+$curtime = formtime2time(date('H:i'));
273+echo "<!-- today={$today}; day={$day}; curtime={$curtime} -->\n";
274+while ($row = $sqlres->fetcharray()) {
275+ $classname = 'event';
276+ if ($day < $today) {
277+ $classname = 'eventpast';
278+ } else if ($day == $today) {
279+ if ($row['timeto'] < $curtime) $classname = 'eventpast';
280+ }
281+ echo ' <a href="?a=event&amp;id=' . $row['id'] . '&amp;day=' . $day . '"><div class="' . $classname . '"><span class="timeofday">' . time2formtime($row['timefrom']) . ' - ' . time2formtime($row['timeto']) . '</span> ' . htmlspecialchars($row['title']) . "</div></a>\n";
282+ $eventsfound++;
283+}
284+$sqlres->finalize();
285+
286+if ($eventsfound === 0) {
287+ echo '<div class="nothingmsg">No scheduled events</div>' . "\n";
288+}
289+
290+
291+// display todos
292+$todofound = 0;
293+$sqlres = $db->query("SELECT * FROM todos WHERE notbefore <= {$day} AND doneday <= {$day} ORDER BY notbefore, title;");
294+while ($row = $sqlres->fetcharray()) {
295+ if ($todofound === 0) {
296+ $todofound = 1;
297+ echo '<div class="columnsectitle">pending tasks</div>' . "\n";
298+ }
299+ echo ' <a href="?a=todo&amp;day=' . $day . '&amp;id=' . $row['id'] . '"><div class="todo">' . htmlspecialchars($row['title']) . "</div></a>\n";
300+}
301+$sqlres->finalize();
302+echo "</div>\n";
303+
304+// toolbar
305+echo '<div class="toolscolumn">' . "\n";
306+echo " <a href=\"?a=event&amp;day={$day}\">+event</a> <a href=\"?a=memo&amp;day={$day}\">+memo</a> <a href=\"?a=todo&amp;day={$day}\">+todo</a>\n";
307+echo "</div>\n";
308+
309+
310+GAMEOVER:
311+?>
312+
313+</body>
314+</html>
--- trunk/pworg.txt (nonexistent)
+++ trunk/pworg.txt (revision 3)
@@ -0,0 +1,4 @@
1+
2+pworg: Personal Web Organizer
3+
4+MIT License
--- trunk/style.css (nonexistent)
+++ trunk/style.css (revision 3)
@@ -0,0 +1,207 @@
1+@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap');
2+
3+html {
4+ background: #eee;
5+}
6+
7+body {
8+ width: 100%;
9+ max-width: 35em;
10+ box-sizing: border-box;
11+ margin: 0 auto;
12+ padding: 0;
13+ font-family: 'Roboto';
14+ font-size: 1.1em;
15+}
16+
17+div.column, div.toolscolumn {
18+ box-sizing: border-box;
19+ padding: 0.2em;
20+ width: 100%;
21+ margin: 0.2em 0;
22+ background: #fff;
23+ border: 1px #ccc solid;
24+ border-radius: 0.25em;
25+}
26+
27+div.column a {
28+ text-decoration: none;
29+ color: inherit;
30+}
31+
32+div.toolscolumn {
33+ display: flex;
34+ flex-wrap: wrap;
35+ justify-content: space-evenly;
36+}
37+
38+div.toolscolumn a {
39+ text-decoration: none;
40+ border: 1px #bbb solid;
41+ border-radius: 0.3em;
42+ padding: 0.1em 0.5em;
43+ background: #efd;
44+ color: #00e;
45+}
46+
47+div.columntitle {
48+ font-weight: 700;
49+ font-size: 1.2em;
50+ display: flex;
51+ justify-content: space-around;
52+ align-items: center;
53+ margin-bottom: 1em;
54+}
55+
56+div.columntitle a {
57+ text-decoration: none;
58+ border: 1px #bbb solid;
59+ border-radius: 0.7em;
60+ padding: 0.2em 0.5em;
61+ color: #00e;
62+}
63+
64+div.columntitle div.datefield {
65+ font-weight: 500;
66+ font-size: 0.9em;
67+ text-align: center;
68+ padding: 0;
69+ width: auto;
70+ white-space: nowrap;
71+}
72+
73+div.columntitle div.datefield a {
74+ padding: 0;
75+ color: #00d;
76+ border: none;
77+}
78+
79+div.columnsectitle {
80+ font-weight: 500;
81+ text-align: center;
82+ font-size: 1.05em;
83+ margin-top: 1em;
84+}
85+
86+div.event, div.eventpast, div.memo, div.todo {
87+ margin: 0.2em 0;
88+ border: 1px #ccc solid;
89+ border-radius: 0.25em;
90+ width: 100%;
91+ box-sizing: border-box;
92+ padding: 0 0.25em;
93+}
94+
95+div.event {
96+ background: #ffd;
97+}
98+
99+div.todo {
100+ background: #ffd;
101+}
102+
103+div.eventpast {
104+ color: #222;
105+ background: #eec;
106+}
107+
108+div.memo {
109+ background: #fed;
110+}
111+
112+span.timeofday {
113+ font-weight: bold;
114+}
115+
116+form input, textarea {
117+ margin: 0;
118+ margin-bottom: 0.8em;
119+ width: 100%;
120+ box-sizing: border-box;
121+ border: 1px #bbb solid;
122+ border-radius: 0.2em;
123+}
124+
125+form div.timerange input {
126+ width: auto;
127+}
128+
129+form textarea {
130+ resize: vertical;
131+ height: 8em;
132+}
133+
134+form div.btns {
135+ display: flex;
136+ flex-wrap: wrap;
137+ justify-content: space-evenly;
138+ margin-bottom: 1em;
139+}
140+
141+form div.btns input, form div.btns a {
142+ font-family: inherit;
143+ color: #000;
144+ padding: 0.2em 0.5em;
145+ text-decoration: none;
146+ background: #efd;
147+ width: auto;
148+ margin: 0;
149+ font-size: 0.9em;
150+ border: 1px #bbb solid;
151+ border-radius: 0.25em;
152+}
153+
154+div.nothingmsg {
155+ color: #777;
156+ text-align: center;
157+ margin: 2em 0 3em 0;
158+ width: 100%;
159+}
160+
161+
162+/* DARK MODE */
163+
164+@media (prefers-color-scheme: dark) {
165+ html {
166+ background: #111;
167+ }
168+
169+ body {
170+ color: #fff;
171+ }
172+
173+ div.column, div.toolscolumn {
174+ background: #000;
175+ }
176+
177+ div.event {
178+ background: #222;
179+ }
180+
181+ div.eventpast {
182+ color: #666;
183+ background: #111;
184+ }
185+
186+ div.todo {
187+ background: #111;
188+ }
189+
190+ div.memo {
191+ background: #411;
192+ }
193+
194+ form div.btns input, form div.btns a {
195+ background: #343;
196+ }
197+
198+ div.toolscolumn a {
199+ background: #231;
200+ color: #77f;
201+ }
202+
203+ a {
204+ color: #77f;
205+ }
206+
207+}
Show on old repository browser