diff --git a/db/mysql/create_links_table.sql b/db/mysql/create_links_table.sql new file mode 100644 index 0000000..751217f --- /dev/null +++ b/db/mysql/create_links_table.sql @@ -0,0 +1,8 @@ +CREATE table if not exists links ( + id integer primary key auto_increment, + label text not null, + url text not null, + icon text not null, + position text not null, + visibility text null, + sort integer not null default 0) diff --git a/db/mysql/create_posts_table.sql b/db/mysql/create_posts_table.sql new file mode 100644 index 0000000..2e3f623 --- /dev/null +++ b/db/mysql/create_posts_table.sql @@ -0,0 +1,9 @@ +CREATE table if not exists posts ( + id integer primary key auto_increment, + username text not null, + content text not null, + location text not null, + visibility text null, + created timestamp not null default current_timestamp, + updated timestamp not null default current_timestamp, + sort integer not null default 0) diff --git a/db/mysql/create_sessions_table.sql b/db/mysql/create_sessions_table.sql new file mode 100644 index 0000000..23748b6 --- /dev/null +++ b/db/mysql/create_sessions_table.sql @@ -0,0 +1,5 @@ +CREATE table if not exists sessions ( + id integer primary key auto_increment, + username text not null, + token text not null, + expires timestamp null) diff --git a/db/mysql/create_settings_table.sql b/db/mysql/create_settings_table.sql new file mode 100644 index 0000000..80a3931 --- /dev/null +++ b/db/mysql/create_settings_table.sql @@ -0,0 +1,4 @@ +CREATE table if not exists settings ( + id integer primary key auto_increment, + setting text not null, + value text not null) diff --git a/db/mysql/create_users_table.sql b/db/mysql/create_users_table.sql new file mode 100644 index 0000000..82f7cbd --- /dev/null +++ b/db/mysql/create_users_table.sql @@ -0,0 +1,7 @@ +CREATE table if not exists users ( + id integer primary key auto_increment, + username text not null, + hash text not null, + can_post integer not null default 0, + is_admin integer not null default 0, + created timestamp not null default current_timestamp) diff --git a/db/mysql/get_last_post.sql b/db/mysql/get_last_post.sql new file mode 100644 index 0000000..57c68ee --- /dev/null +++ b/db/mysql/get_last_post.sql @@ -0,0 +1,4 @@ +SELECT * +from posts +where + id = last_insert_id() diff --git a/db/sqlite/create_links_table.sql b/db/sqlite/create_links_table.sql new file mode 100644 index 0000000..d6be4b1 --- /dev/null +++ b/db/sqlite/create_links_table.sql @@ -0,0 +1,8 @@ +CREATE table if not exists links ( + id integer primary key autoincrement, + label text not null, + url text not null, + icon text not null, + position text not null, + visibility text null, + sort integer not null default 0) diff --git a/db/sqlite/create_posts_table.sql b/db/sqlite/create_posts_table.sql new file mode 100644 index 0000000..c031c5a --- /dev/null +++ b/db/sqlite/create_posts_table.sql @@ -0,0 +1,9 @@ +CREATE table if not exists posts ( + id integer primary key autoincrement, + username text not null, + content text not null, + location text not null, + visibility text null, + created timestamp not null default current_timestamp, + updated timestamp not null default current_timestamp, + sort integer not null default 0) diff --git a/db/sqlite/create_sessions_table.sql b/db/sqlite/create_sessions_table.sql new file mode 100644 index 0000000..d93597a --- /dev/null +++ b/db/sqlite/create_sessions_table.sql @@ -0,0 +1,5 @@ +CREATE table if not exists sessions ( + id integer primary key autoincrement, + username text not null, + token text not null, + expires timestamp null) diff --git a/db/sqlite/create_settings_table.sql b/db/sqlite/create_settings_table.sql new file mode 100644 index 0000000..e0a9139 --- /dev/null +++ b/db/sqlite/create_settings_table.sql @@ -0,0 +1,4 @@ +CREATE table if not exists settings ( + id integer primary key autoincrement, + setting text not null, + value text not null) diff --git a/db/sqlite/create_users_table.sql b/db/sqlite/create_users_table.sql new file mode 100644 index 0000000..dd608c1 --- /dev/null +++ b/db/sqlite/create_users_table.sql @@ -0,0 +1,7 @@ +CREATE table if not exists users ( + id integer primary key autoincrement, + username text not null, + hash text not null, + can_post integer not null default 0, + is_admin integer not null default 0, + created timestamp not null default current_timestamp) diff --git a/db/sqlite/get_last_post.sql b/db/sqlite/get_last_post.sql new file mode 100644 index 0000000..abbeca0 --- /dev/null +++ b/db/sqlite/get_last_post.sql @@ -0,0 +1,4 @@ +SELECT * +from posts +where + rowid = last_insert_rowid() diff --git a/files/FormValidator/FormValidator.js b/files/FormValidator/FormValidator.js new file mode 100644 index 0000000..917cbcf --- /dev/null +++ b/files/FormValidator/FormValidator.js @@ -0,0 +1,46 @@ +$(function() { + FormValidator = {}; + + FormValidator.onError = function(str) + { + alert(str); + console.log(str); + }; + + FormValidator.validate = function(callback) + { + var pass = 1; + + $("[fv-regex]").each(function(i, x) { + + if (pass == 0) + return; + + x = $(x); + + let input = x.val(); + let regex = new RegExp(x.attr("fv-regex"), "g"); + + if (!input.match(regex)) + { + x.addClass("border-danger"); + x.focus(); + x.select(); + + let warning = x.attr("fv-warning") ?? "Please correct the highlighted input and try again"; + + FormValidator.onError(warning) + + pass = 0; + return; + } + + if (pass == 1) + if (typeof callback === "function") + callback(); + + x.removeClass("border-danger"); + }); + + }; +}); diff --git a/files/SearchableInput/SearchableInput.css b/files/SearchableInput/SearchableInput.css new file mode 100644 index 0000000..a3ec784 --- /dev/null +++ b/files/SearchableInput/SearchableInput.css @@ -0,0 +1,37 @@ +div.searchable-input input +{ + width: 100%; + box-sizing: border-box; +} + +div.searchable-input div.items +{ + padding-top: 0.5em; + padding-bottom: 0.5em; + border: 1px solid #aaa; + /*border-top: 0;*/ + max-width: 100%; + max-height: 10em; + overflow-x: scroll; + overflow-y: scroll; + position: absolute; + background: #fff; + box-sizing: border-box; +} + +div.searchable-input div.item +{ + white-space: normal; + padding-left: 0.5em; + padding-right: 0.5em; + user-select: none; +} + +div.searchable-input div.item:hover +{ + /*background: #0af; + color: #fff;*/ + background: highlight; + color: highlighttext; + cursor: pointer; +} diff --git a/files/SearchableInput/SearchableInput.js b/files/SearchableInput/SearchableInput.js new file mode 100644 index 0000000..bf0e050 --- /dev/null +++ b/files/SearchableInput/SearchableInput.js @@ -0,0 +1,206 @@ +$(function() { + SearchableInput = {}; + + SearchableInput.initItems = function() + { + var items = $("div.searchable-input div.item"); + + items.each(function(i, x) { + x = $(x); + + var parent = x.parents("div.searchable-input").first(); + var input = parent.find("input").first(); + var item = x; + var itemContainer = parent.find("div.items").first(); + + if (item.is("[onclick]")) + return; + + if (!item.is("[data-value]")) + return; + + item.unbind("mousedown"); + item.bind("mousedown", function() { + input.attr("data-value", item.attr("data-value")); + input.val(item.text().trim()); + itemContainer.hide(); + }); + }); + }; + + SearchableInput.init = function() + { + $("div.searchable-input input").each(function(i, x) { + x = $(x); + + var parent = x.parents("div.searchable-input").first(); + var backgroundColor = null; + var selectColor = null; + var input = x; + var itemContainer = parent.find("div.items").first(); + var items = itemContainer.find("div.item"); + var strictSearch = parent.attr("strict-search"); + + // Get first opaque body color: + parent.parents("*").each(function(i, x) { + if (backgroundColor !== null) + return; + + x = $(x); + var color = x.css("backgroundColor"); + + if (/^rgba/.test(color)) + { + var alphaPart = parseFloat(color.split(",")[3]); + + if (isNaN(alphaPart)) + return; + + if (alphaPart > 0.9) + backgroundColor = color; + + return; + } + + if (/^rgb/.test(color)) + backgroundColor = color; + }); + + itemContainer.hide(); + + x.unbind("blur"); + x.bind("blur", function() { + setTimeout(function() { + itemContainer.hide(); + + if (input.val().trim().length < 1) + input.attr("data-value", null); + }, 10); + }); + + x.unbind("input"); + x.bind("input", function() { + + var fnOnInputCooldown = function(itemContainer) + { + var parent = itemContainer.parents("div.searchable-input").first(); + var input = parent.find("input").first(); + var items = itemContainer.find("div.item"); + var searchTermsString = input.val().toLowerCase().trim(); + var searchTerms = searchTermsString.split(" "); + + items.hide(); + + items.each(function(i, x) { + x = $(x); + + var item = x; + var pass = 1; + var itemText = item.text().toLowerCase().trim(); + + if (strictSearch) + { + if (itemText.includes(searchTermsString)) + item.show(); + return; + } + + for (var i = 0; i < searchTerms.length; i++) + { + var searchTerm = searchTerms[i]; + if (searchTerm.length < 1) + continue; + if (!itemText.includes(searchTerm)) + pass = 0; + } + + //item.hide(); + + if (pass == 1) + item.show(); + }); + }; + + + var dataSourceApi = itemContainer.attr("data-source-api"); + + if (dataSourceApi) + { + if (typeof(SearchableInput.timeout) !== "undefined") + clearTimeout(SearchableInput.timeout); + + SearchableInput.timeout = setTimeout(function() { + var searchTermsString = input.val().toLowerCase().trim(); + + console.log(dataSourceApi); + + $.get({ + url: `${dataSourceApi}?q=${searchTermsString}`, + //url: `${dataSourceApi}`, + method: "get", + success: function(data) + { + console.log(data); + itemContainer.html(""); + + for (var i = 0; i < data.length; i++) + { + var item = data[i]; + var itemElement = $("
"); + + itemElement.addClass("item"); + itemElement.attr("data-value", item.value); + itemElement.html(item.name); + + itemContainer.append(itemElement); + } + + SearchableInput.initItems(); + + fnOnInputCooldown(itemContainer); + }, + error: function(xhr, e, m) + { + console.log(e); + console.log(m); + } + }); + }, 1000); + } + + fnOnInputCooldown(itemContainer); + }); + + x.unbind("focus"); + x.bind("focus", function() { + //x.trigger("input"); + + itemContainer.css("width", parent.width() + "px"); + itemContainer.css("background", backgroundColor); + + // If data-source is a valid jQuery selector, use that container's innerHTML: + var dataSource = itemContainer.attr("data-source"); + + if (dataSource) + { + var dataSourceObject = $(dataSource); + if (dataSourceObject.length > 0) + { + itemContainer.html(dataSourceObject.first().html()); + //SearchableInput.init(); + SearchableInput.initItems(); + } + } + + itemContainer.show(); + items.show(); + input.select(); + + }); + + SearchableInput.initItems(); + }); + }; + + SearchableInput.init(); +}); diff --git a/footer.php b/footer.php index 13b9c51..1754c3f 100644 --- a/footer.php +++ b/footer.php @@ -1,5 +1,6 @@ query("SELECT * from links where position like 'footer' order by sort"); ?> @@ -9,6 +10,8 @@
+ +
"> pe-2">
@@ -16,3 +19,24 @@
+ + + + diff --git a/head.php b/head.php index 753087f..a450009 100644 --- a/head.php +++ b/head.php @@ -1,10 +1,35 @@ - + + + + + + + + + + + + + + diff --git a/header.php b/header.php index ddbdbbd..544421a 100644 --- a/header.php +++ b/header.php @@ -1,17 +1,11 @@ query("SELECT * from links where position like 'navbar' order by sort"); $varSidebarLinks = $c->query("SELECT * from links where position like 'sidebar' order by sort"); $varFirstNavbarLink = array_shift($varNavbarLinks); ?> - - + + + + - + + return messageElem; + }; + }); + + +
+ + < + type="" + class="form-control" + name="" + placeholder="Enter " + + value="" + + $v): ?> + ="" + + />" : ""; ?> + +
+ + public static function button($varOptions) + { + $varOptions["tag"] = $varOptions["tag"] ?? "a"; + $varOptionsExtras = $varOptions; + $varDefaultKeys = ["tag", "label", "name", "type", "value", "hint"]; -
-
- -
+ foreach ($varDefaultKeys as $k) + if (array_key_exists($k, $varOptionsExtras)) + unset($varOptionsExtras[$k]); + ?> + < + class="btn btn-" + $v): ?> + ="" + + > + + "> + -
- < type="" - class="form-control" - name="" - placeholder="Enter " - value="" - - - - - >> - - /> - -
- -
- test -
-
- - + + > +
-
- < type="" - class="form-control" - name="" - placeholder="Enter " - value="" - - - - - >> - - /> - +
+ +
- - - - + +
@@ -19,7 +19,7 @@

-
by
+
by
on UTC
@@ -30,9 +30,11 @@
+ + - +
@@ -41,10 +43,6 @@
- - - - - - - -
- - - -
-
- -
-
-
- - diff --git a/pages/edit/links.php b/pages/edit/links.php index c801dc9..6ca1544 100644 --- a/pages/edit/links.php +++ b/pages/edit/links.php @@ -1,5 +1,5 @@ diff --git a/pages/index.php b/pages/index.php index 7de96a9..00d3d42 100644 --- a/pages/index.php +++ b/pages/index.php @@ -5,59 +5,16 @@ $strPath .= implode("/", Request::getPathParts()); $varPosts = $c->query( - "SELECT - p.*, - u.user_name, - u.display_name + "SELECT * from posts as p - left join users as u on u.email = p.email where - path like ? - or path like '*' + location like ? + or location like '*' order by created desc", $strPath); - - $strSearchQuery = Request::getParam("q"); - - if ($strSearchQuery) - { - $varPosts = $c->query( - "SELECT - p.*, - u.user_name, - u.display_name - from posts as p - left join users as u on u.email = p.email - where - content like concat('%', ?, '%') - order by - created desc", - $strSearchQuery); - } - - - $varParsedown = new Parsedown(); ?> - - - -
diff --git a/pages/post/index.php b/pages/post/index.php index ee0ccec..7286fa0 100644 --- a/pages/post/index.php +++ b/pages/post/index.php @@ -1,13 +1,15 @@ 0) { @@ -20,27 +22,30 @@ Respond::redirect("/post"); } - $varRow = $varRows[0]; - $strPath = $varRow["path"]; - $strContent = $varRow["content"]; + $varRow = $varRows[0]; + $strContent = $varRow["content"]; + $strLocation = $varRow["location"]; + $strVisibility = $varRow["visibility"]; } - if (Request::posts("path", "content")) + if (Request::posts("location", "content", "visibility")) { - $strPath = Request::getPosted("path"); - $strContent = Request::getPosted("content"); + $strLocation = Request::getPosted("location"); + $strContent = Request::getPosted("content"); + $strVisibility = Request::getPosted("visibility"); if ($strId == null || strlen($strId) < 1) { $c->query( - "INSERT into posts (email, path, content) - values (?, ?, ?)", - $varUser["email"], - $strPath, - $strContent); + "INSERT into posts (username, content, location, visibility) + values (?, ?, ?, ?)", + $varUser["username"], + $strContent, + $strLocation, + $strVisibility); - $strId = $c->query("SELECT * from posts where rowid = last_insert_rowid()")[0]["id"]; + $strId = $c->query("get_last_post.sql")[0]["id"]; } if (strlen($strContent) < 1) @@ -53,15 +58,18 @@ $c->query( "UPDATE posts set - path = ?, - content = ?, - updated = current_timestamp + content = ?, + location = ?, + visibility = ?, + updated = current_timestamp where id = ?", - $strPath, $strContent, + $strLocation, + $strVisibility, $strId); + BootstrapRender::message("Post saved.", "success"); Respond::redirect("/post/{$strId}"); } ?> @@ -77,35 +85,75 @@
-
+
- +
+ +
+ +
+
+ + + Test +
+
+ +
+
+ + + e.g. /home or /info +
+
+ +
+
+ + + e.g. everyone, users, admins +
+
+ +
+
+ +
+ + Submit + + Save + +
+
+
@@ -113,10 +161,18 @@