diff --git a/README.md b/README.md
index e0d208d..59489da 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,35 @@
-# forum.com
-The angularjs front-end for my forum, recently moved from inkletblot.com
+# README for inkletblot.com
+
+This is an angularjs project, currently it is a total rebuild of inkletblot.com using the framework.
+In the future the forum will be separated from the gallery and 'things' pages.
+
+> I know things is a stupid name, can you come up with a better one?
+
+## TODO!
+
+### General:
+* Fix server responses to be all the same.
+ * this is kinda done, need to make all .sends() the same.
+* Fix client catching of server errors.
+* Generally strengthen server API, especially once forum is separate.
+
+### Forum:
+* ***Create Category, both client and server.***
+* ***Create Topic, both client and server.***
+* **Create Post/Reply, both client and server.**
+* ***Fix and test signup.***
+* Create edit post functionality.
+* Create delete post functionality.
+* Menu only shows applicable items based on user level.
+ * Update server/client/cookie to include all user info.
+* implement correct session with server.
+ * reduce clients reliance on cookie info to prevent security threats.
+
+### Gallery/Things:
+*Deal with this once forum is totally complete, it is now main project*
+
+## Interesting Ideas.
+* Implement a text editor for posts?
+* Add an account page for users to manage their accounts.
+* Add admin ability to remove topics and categories.
+* Figure out passing information down the line so that the post page shows the category as well as topic.
\ No newline at end of file
diff --git a/favicon.ico b/favicon.ico
new file mode 100644
index 0000000..abeb8dd
Binary files /dev/null and b/favicon.ico differ
diff --git a/forum_bld.sql b/forum_bld.sql
new file mode 100644
index 0000000..5901869
--- /dev/null
+++ b/forum_bld.sql
@@ -0,0 +1,43 @@
+DROP TABLE posts;
+DROP TABLE topics;
+DROP TABLE categories;
+DROP TABLE users;
+
+CREATE TABLE categories (
+catNo INT(8) NOT NULL AUTO_INCREMENT,
+catName VARCHAR(255) NOT NULL,
+catDescr VARCHAR(255) NOT NULL,
+PRIMARY KEY (catNo)
+);
+
+CREATE TABLE users (
+userNo INT(8) NOT NULL AUTO_INCREMENT,
+userName VARCHAR(255) NOT NULL,
+userPass VARCHAR(255) NOT NULL,
+userEmail VARCHAR(255) NOT NULL,
+userDate DATETIME NOT NULL,
+userLevel INT(8) NOT NULL,
+PRIMARY KEY (userNo)
+);
+
+CREATE TABLE topics (
+topicNo INT(8) NOT NULL AUTO_INCREMENT,
+topicSubject VARCHAR(255) NOT NULL,
+topicDate DATETIME NOT NULL,
+topicCat INT(8) NOT NULL,
+topicBy INT(8) NOT NULL,
+PRIMARY KEY (topicNo),
+FOREIGN KEY (topicCat) REFERENCES categories (catNo),
+FOREIGN KEY (topicBy) REFERENCES users (userNo)
+);
+
+CREATE TABLE posts (
+postNo INT(8) NOT NULL AUTO_INCREMENT,
+postContent VARCHAR(255) NOT NULL,
+postDate DATETIME NOT NULL,
+postTopic INT(8) NOT NULL,
+postBy INT(8) NOT NULL,
+PRIMARY KEY (postNo),
+FOREIGN KEY (postTopic) REFERENCES topics (topicNo),
+FOREIGN KEY (postBy) REFERENCES users (userNo)
+);
\ No newline at end of file
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..dcc7592
--- /dev/null
+++ b/index.html
@@ -0,0 +1,38 @@
+
+
+
+
+
+ Inklet's Space
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..723fd5f
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,595 @@
+{
+ "name": "forum.inkletblot.com",
+ "version": "1.0.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "accepts": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
+ "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
+ "requires": {
+ "mime-types": "~2.1.24",
+ "negotiator": "0.6.2"
+ }
+ },
+ "array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+ },
+ "async": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
+ "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+ "requires": {
+ "lodash": "^4.17.14"
+ }
+ },
+ "body-parser": {
+ "version": "1.19.0",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
+ "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
+ "requires": {
+ "bytes": "3.1.0",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "http-errors": "1.7.2",
+ "iconv-lite": "0.4.24",
+ "on-finished": "~2.3.0",
+ "qs": "6.7.0",
+ "raw-body": "2.4.0",
+ "type-is": "~1.6.17"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ },
+ "qs": {
+ "version": "6.7.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+ "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
+ }
+ }
+ },
+ "bytes": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
+ "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
+ },
+ "colors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
+ "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs="
+ },
+ "content-disposition": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
+ "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
+ "requires": {
+ "safe-buffer": "5.1.2"
+ }
+ },
+ "content-type": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
+ },
+ "cookie": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
+ "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
+ },
+ "cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+ },
+ "corser": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz",
+ "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c="
+ },
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha1-6D0X3hbYp++3cX7b5fsQE17uYps=",
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+ },
+ "destroy": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+ },
+ "ecstatic": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.3.2.tgz",
+ "integrity": "sha1-bR3UmBTQBZRoLGUq22YHamnUbEg=",
+ "requires": {
+ "he": "^1.1.1",
+ "mime": "^1.6.0",
+ "minimist": "^1.1.0",
+ "url-join": "^2.0.5"
+ }
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+ },
+ "encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+ },
+ "etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
+ },
+ "eventemitter3": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz",
+ "integrity": "sha1-1lF2FjiH7lnzhtZMgmELaWpKdOs="
+ },
+ "express": {
+ "version": "4.17.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
+ "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
+ "requires": {
+ "accepts": "~1.3.7",
+ "array-flatten": "1.1.1",
+ "body-parser": "1.19.0",
+ "content-disposition": "0.5.3",
+ "content-type": "~1.0.4",
+ "cookie": "0.4.0",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "~1.1.2",
+ "fresh": "0.5.2",
+ "merge-descriptors": "1.0.1",
+ "methods": "~1.1.2",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "0.1.7",
+ "proxy-addr": "~2.0.5",
+ "qs": "6.7.0",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.1.2",
+ "send": "0.17.1",
+ "serve-static": "1.14.1",
+ "setprototypeof": "1.1.1",
+ "statuses": "~1.5.0",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ },
+ "qs": {
+ "version": "6.7.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+ "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
+ }
+ }
+ },
+ "finalhandler": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+ "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+ "requires": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.3",
+ "statuses": "~1.5.0",
+ "unpipe": "~1.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ }
+ }
+ },
+ "follow-redirects": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.9.0.tgz",
+ "integrity": "sha1-jVvNxltxCP4VCGScecEtcy3O208=",
+ "requires": {
+ "debug": "^3.0.0"
+ }
+ },
+ "forwarded": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
+ "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
+ },
+ "fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
+ },
+ "he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha1-hK5l+n6vsWX922FWauFLrwVmTw8="
+ },
+ "http-errors": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
+ "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.1",
+ "statuses": ">= 1.5.0 < 2",
+ "toidentifier": "1.0.0"
+ }
+ },
+ "http-proxy": {
+ "version": "1.18.0",
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz",
+ "integrity": "sha1-2+VfY+daNH2389mZdPJpKjFKajo=",
+ "requires": {
+ "eventemitter3": "^4.0.0",
+ "follow-redirects": "^1.0.0",
+ "requires-port": "^1.0.0"
+ }
+ },
+ "http-server": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.11.1.tgz",
+ "integrity": "sha512-6JeGDGoujJLmhjiRGlt8yK8Z9Kl0vnl/dQoQZlc4oeqaUoAKQg94NILLfrY3oWzSyFaQCVNTcKE5PZ3cH8VP9w==",
+ "requires": {
+ "colors": "1.0.3",
+ "corser": "~2.0.0",
+ "ecstatic": "^3.0.0",
+ "http-proxy": "^1.8.1",
+ "opener": "~1.4.0",
+ "optimist": "0.6.x",
+ "portfinder": "^1.0.13",
+ "union": "~0.4.3"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ },
+ "ipaddr.js": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
+ "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA=="
+ },
+ "lodash": {
+ "version": "4.17.15",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
+ },
+ "media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
+ },
+ "merge-descriptors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+ },
+ "methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
+ },
+ "mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE="
+ },
+ "mime-db": {
+ "version": "1.40.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
+ "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA=="
+ },
+ "mime-types": {
+ "version": "2.1.24",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
+ "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
+ "requires": {
+ "mime-db": "1.40.0"
+ }
+ },
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "requires": {
+ "minimist": "0.0.8"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+ }
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk="
+ },
+ "negotiator": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
+ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
+ },
+ "on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "opener": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/opener/-/opener-1.4.3.tgz",
+ "integrity": "sha1-XG2ixdflgx6P+jlklQ+NZnSskLg="
+ },
+ "optimist": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+ "requires": {
+ "minimist": "~0.0.1",
+ "wordwrap": "~0.0.2"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
+ "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
+ }
+ }
+ },
+ "parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
+ },
+ "path-to-regexp": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+ },
+ "portfinder": {
+ "version": "1.0.25",
+ "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz",
+ "integrity": "sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg==",
+ "requires": {
+ "async": "^2.6.2",
+ "debug": "^3.1.1",
+ "mkdirp": "^0.5.1"
+ }
+ },
+ "proxy-addr": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
+ "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==",
+ "requires": {
+ "forwarded": "~0.1.2",
+ "ipaddr.js": "1.9.0"
+ }
+ },
+ "qs": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz",
+ "integrity": "sha1-6eha2+ddoLvkyOBHaghikPhjtAQ="
+ },
+ "range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
+ },
+ "raw-body": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
+ "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
+ "requires": {
+ "bytes": "3.1.0",
+ "http-errors": "1.7.2",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ }
+ },
+ "requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "send": {
+ "version": "0.17.1",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
+ "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
+ "requires": {
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "destroy": "~1.0.4",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "~1.7.2",
+ "mime": "1.6.0",
+ "ms": "2.1.1",
+ "on-finished": "~2.3.0",
+ "range-parser": "~1.2.1",
+ "statuses": "~1.5.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "requires": {
+ "ms": "2.0.0"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ }
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+ }
+ }
+ },
+ "serve-static": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
+ "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
+ "requires": {
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "0.17.1"
+ }
+ },
+ "setprototypeof": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+ "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
+ },
+ "statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
+ },
+ "toidentifier": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
+ },
+ "type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "requires": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ }
+ },
+ "union": {
+ "version": "0.4.6",
+ "resolved": "https://registry.npmjs.org/union/-/union-0.4.6.tgz",
+ "integrity": "sha1-GY+9rrolTniLDvy2MLwR8kopWeA=",
+ "requires": {
+ "qs": "~2.3.3"
+ }
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+ },
+ "url-join": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz",
+ "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg="
+ },
+ "utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
+ },
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
+ },
+ "wordwrap": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+ "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..c5b6bcc
--- /dev/null
+++ b/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "forum.inkletblot.com",
+ "version": "1.0.0",
+ "description": "angularjs implementation of my forum on inkletblot.com",
+ "main": "server.js",
+ "dependencies": {
+ "cors": "^2.8.5",
+ "express": "^4.17.1"
+ },
+ "devDependencies": {},
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "Solomon Laing",
+ "license": "MIT"
+}
diff --git a/scripts/auth.controllers.js b/scripts/auth.controllers.js
new file mode 100644
index 0000000..5037e1e
--- /dev/null
+++ b/scripts/auth.controllers.js
@@ -0,0 +1,158 @@
+daddy.controller("loginCtrlr", function($scope, $cookies, $location, $http, stateData){
+
+ let titles = {
+ login : "Login!",
+ signup : "Not a Member?"
+ }
+ $scope.titles = titles
+
+ let message = {
+ start : "Feel free to",
+ link : "Sign Up",
+ end : "at any time!"
+ }
+ $scope.message = message
+
+ $scope.setSignup = () => {
+ stateData.setPage("things")
+ $location.path("/signup")
+ }
+
+ $scope.login = () => {
+ $scope.status = true
+ $scope.loginStatus = "Logging you in..."
+ console.log("logging in")
+ console.log("requesting authentication from server")
+ $http({
+ url : server + "/auth/login?userName="+$scope.user.username+"&userPass="+$scope.user.password,
+ method : "GET"
+ }).then((res) => {
+ console.log(res)
+ if (res.status == 200) {
+ console.log("request granted")
+ let today = new Date()
+ let expired = new Date(today)
+ expired.setDate(today.getDate()+1)
+ $cookies.putObject('user', res.data[0], {expires : expired})
+ console.log("set cookie")
+ stateData.setUser(res.data[0])
+ console.log("set data")
+ stateData.setPage("things")
+ $location.path("/things")
+ }
+ }).catch((res) => {
+ if (res.status == 400) {
+ $scope.loginStatus = "Incorrect information!"
+ console.log("request denied")
+ } else if (res.status == 500) {
+ console.log("something went wrong: " + res)
+ }
+ })
+ }
+
+})
+
+daddy.controller("signupCtrlr", function($scope, $http, $location, stateData) {
+
+ let titles = {
+ form : "Sign up here!",
+ success : "Signup Success!"
+ }
+ $scope.titles = titles
+
+ let message = {
+ start : "You account has been created!",
+ line2 : "Feel free to",
+ link : "Login",
+ end : "whenever you like."
+ }
+ $scope.message = message
+
+ $scope.success = false
+ $scope.exists = false
+ $scope.status = false
+
+ $scope.setLogin = () => {
+ stateData.setPage("things")
+ $location.path("/login")
+ }
+
+ $scope.signup = () => {
+ $scope.userExists()
+ $scope.passwordsMatch()
+ $scope.emailGood()
+
+ if ($scope.match && !$scope.exists && $scope.goodemail) {
+ $scope.status = false
+ $scope.submit()
+ } else {
+ $scope.status = true
+ $scope.signupStatus = "Please correct errors above."
+ }
+ }
+
+ $scope.emailGood = () => {
+ if (validateEmail($scope.user.email)) {
+ $scope.goodemail = true
+ $scope.emailError = ""
+ } else {
+ $scope.goodemail = false
+ $scope.emailError = "< invalid email."
+ }
+ }
+
+ $scope.passwordsMatch = () => {
+ if ($scope.user.password != $scope.user.confirmPassword) {
+ $scope.match = false
+ $scope.passwordError = "< password does not match."
+ } else {
+ $scope.match = true
+ $scope.passwordError = ""
+ }
+ }
+
+ $scope.submit = () => {
+ $http({
+ url : server + "/auth/signup?userName=" + $scope.user.username + "&userPass=" + $scope.user.password + "&userEmail=" + $scope.user.email,
+ method : "POST"
+ }).then((res) => {
+ if (res.status == 200) {
+ console.log("user created successfully")
+ $scope.success = true
+ $scope.signupForm.$setPristine()
+ $scope.user.username = ""
+ $scope.user.password = ""
+ $scope.user.confirmPassword = ""
+ $scope.user.email = ""
+ }
+ }).catch((res) => {
+ if (res.status == 500) {
+ console.log("whoops...")
+ }
+ })
+ }
+
+ $scope.userExists = () => {
+ console.log("checking username is used?")
+ $http({
+ url : server + "/auth/exists?userName=" + $scope.user.username,
+ method : "POST"
+ }).then((res) => {
+ if (res.status == 200) {
+ console.log(res.data)
+ if (res.data == true) {
+ $scope.exists = true
+ $scope.usernameError= "< username already exists."
+ } else {
+ $scope.exists = false
+ $scope.usernameError = ""
+ }
+ }
+ }).catch((res) => {
+ if (res.status == 500) {
+ console.log ("something went wrong")
+ }
+ })
+ }
+
+})
\ No newline at end of file
diff --git a/scripts/forum.controller.js b/scripts/forum.controller.js
new file mode 100644
index 0000000..487f7e2
--- /dev/null
+++ b/scripts/forum.controller.js
@@ -0,0 +1,600 @@
+daddy.controller("forumCtrlr", function($scope, $http, $location) {
+
+ $scope.titles = {
+ main : {
+ heading : "Welcome!",
+ },
+ recent : {
+ heading : "Recent Posts",
+ list : "Catergory",
+ last : "Last Topic"
+ }
+ }
+
+ $scope.errors = {
+ topic : {
+ show : false,
+ text : "None yet!"
+ },
+ catagory : {
+ show : false,
+ text: "None yet!"
+ }
+ }
+
+ $scope.getCategories = () => {
+ console.log("getting categories form server")
+ $http({
+ url : server + "/forum/category/all",
+ method : "GET"
+ }).then((res) => {
+ if (res.status == 200) {
+ if (res.data.length == 0) {
+ $scope.errors.catagory.show = true
+ } else {
+ $scope.categories = res.data
+ for (let i = 0; i < res.data.length; i++) {
+ $http({
+ url : server + "/forum/category/topic/last?cat=" + res.data[i].catNo,
+ method : "GET"
+ }).then((res) => {
+ if (res.status == 200) {
+ console.log("topicNo: " + res.data.topicNo)
+ // if (res.data.topicNo == null) {
+ // $scope.errors.topic.show = true
+ // } else {
+ res.data.topicDate = niceDate(res.data.topicDate)
+ $scope.categories[i].lastTopic = res.data
+ // }
+ } else {
+ console.log("server has encountered an error: " + res.status)
+ }
+ })
+ }
+ console.log(res)
+ }
+ }
+ }).catch((res) => {
+ console.log("server has encountered an error: " + res.status)
+ })
+ }
+
+ $scope.setCat = (catNo) => {
+ $location.path("/categories/"+catNo)
+ }
+
+ $scope.setTopic = (topicNo) => {
+ $location.path("/topics/"+topicNo)
+ }
+
+})
+
+daddy.controller("createTopicCtrlr", function($scope, $location, $http, stateData) {
+
+ $scope.titles = {
+ form : "Create a topic!"
+ }
+
+ $scope.status = {
+ show : false,
+ text : ""
+ }
+
+ let error = {
+ loggedIn : {
+ show : false,
+ text1 : "You must be",
+ link1 : "Logged In",
+ text2 : "to create a topic!"
+ },
+ userLevel : {
+ show : false,
+ text1 : "No categories have been created, please contact an administrator about creating some.",
+ text2 : "Administrator contact: solomonlaing@inkletblot.com"
+ },
+ category : {
+ show : false
+ }
+ }
+
+ $scope.error = error
+
+ $scope.start = () => {
+ if (stateData.state.user.userName == null) {
+ console.log("not logged in.")
+ error.loggedIn.show = true
+ }
+ if (stateData.state.user.userLevel == 0) {
+ console.log("user not an admin.")
+ error.userLevel.show = true
+ }
+ $scope.getCategories()
+ }
+
+ $scope.getCategories = () => {
+ console.log("getting categories form server")
+ $http({
+ url : server + "/forum/category/all",
+ method : "GET"
+ }).then((res) => {
+ if (res.status == 200) {
+ if (res.data.length == 0) {
+ $scope.errors.catagory.show = true
+ } else {
+ $scope.categories = res.data
+ console.log(res)
+ }
+ }
+ }).catch((res) => {
+ console.log("server has encountered an error: " + res.status)
+ })
+ }
+
+ $scope.setLogin = () => {
+ $location.path("/login")
+ stateData.setPage("things")
+ }
+
+ $scope.createTopic = () => {
+ console.log("sending new topic to server!!")
+ let formData = new FormData()
+ formData.append('topicSubject', $scope.topic.topicSubject)
+ formData.append('topicCat', $scope.topic.topicCat)
+ formData.append('userNo', stateData.state.user.userNo)
+ $http({
+ url : server + "/forum/topic/create",
+ method : "POST",
+ data : formData,
+ headers : { 'Content-Type' : undefined },
+ transformRequest : angular.identity
+ }).then((res) => {
+ if (res.status == 200) {
+ console.log("topic submitted")
+ $scope.createPost($scope.topic.postContent, res.data.topicNo)
+ }
+ }).catch((res) => {
+ if (res.status == 500) {
+ console.log("something went wrong: " + res.data)
+ $scope.status.text = "Something went wrong: " + res.status
+ }
+ })
+ }
+
+ $scope.createPost = (postContent, topicNo) => {
+ console.log("sending new post to server")
+ let formData = new FormData()
+ formData.append('postContent', postContent)
+ formData.append('topicNo', topicNo)
+ formData.append('userNo', stateData.state.user.userNo)
+ $http({
+ url : server + "/forum/topic/post/create",
+ method : "POST",
+ data : formData,
+ headers : { 'Content-Type' : undefined },
+ transformRequest : angular.identity
+ }).then((res) => {
+ if (res.status == 200) {
+ console.log("post added")
+ $location.path("/topics/" + topicNo)
+ }
+ }).catch((res) => {
+ if (res.status == 500) {
+ console.log("something went wrong: " + res.data)
+ $scope.status.text = "Something went wrong: " + res.status
+ }
+ })
+ }
+})
+
+daddy.controller("createCategoryCtrlr", function($scope, $http, $location, stateData) {
+
+ let error = {
+ loggedIn : {
+ show : false,
+ text1 : "You must be",
+ link1 : "Logged In",
+ text2 : "to create a category!"
+ },
+ userLevel : {
+ show : false,
+ text1 : "You must be an administrator to create a category!"
+ }
+ }
+
+ $scope.error = error
+
+ $scope.titles = {
+ form : "Create a Category!"
+ }
+
+ $scope.status = {
+ show : false,
+ text : ""
+ }
+
+ $scope.start = () => {
+ if (stateData.state.user.userName == null) {
+ error.loggedIn.show = true
+ }
+ if (stateData.state.user.userLevel == 0) {
+ error.userLevel.show = true
+ }
+ }
+
+ $scope.setLogin = () => {
+ $location.path("/login")
+ stateData.setPage("things")
+ }
+
+ $scope.createCategory = () => {
+ $scope.status.show = true
+ $scope.status.text = "Adding category..."
+
+ let formData = new FormData()
+ formData.append('catName', $scope.category.catName)
+ formData.append('catDescr', $scope.category.catDescr)
+
+ console.log("sending to server...")
+ $http({
+ url : server + "/forum/category/create",
+ method : "POST",
+ data : formData,
+ headers : { 'Content-Type' : undefined },
+ transformRequest : angular.identity
+ }).then((res) => {
+ if (res.status == 200) {
+ console.log("category submitted")
+ $location.path("/categories/" + res.data.catNo)
+ }
+ }).catch((res) => {
+ if (res.status == 500) {
+ console.log("something went wrong: " + res.data)
+ $scope.status.text = "Something went wrong: " + res.status
+ }
+ })
+ }
+
+})
+
+daddy.controller("topicsCtrlr", function($scope, $routeParams, $timeout, $http, $location, stateData) {
+
+ $scope.error = false
+ $scope.message = {
+ start : null,
+ link : "Go Back!"
+ }
+ $scope.logged = {
+ text1 : "You must be",
+ link1 : "logged in",
+ text2 : "to reply. You can also",
+ link2 : "sign up",
+ text3 : "for an account."
+ }
+ $scope.status = {
+ show : false,
+ text : "",
+ }
+ $scope.first = {
+ show : false,
+ text : "Be the first to create a post, write below..."
+ }
+
+ $scope.getTopic = () => {
+ console.log("getting topic")
+ $http({
+ url : server + "/forum/topic?topic=" + $routeParams.topicNo,
+ method : "GET"
+ }).then((res) => {
+ console.log("got something")
+ if (res.status == 200) {
+ let topic = res.data
+ // if (res.data.posts.length == 0) {
+ // $scope.first.show = true
+ // }
+ console.log("got topics!")
+ for (let i = 0; i < topic.posts.length; i++) {
+ topic.posts[i].postDate = niceDate(topic.posts[i].postDate)
+ }
+ $scope.topic = topic
+ }
+ }).catch((res) => {
+ if (res.status == 404) {
+ console.log(res.data)
+ $scope.error = true
+ $scope.message.start = "No such topic,"
+ } else if (res.status == 500) {
+ console.log(res.data)
+ $scope.error = true
+ $scope.message.start = "An error has occured: " + res.status
+ } else {
+ console.log(res)
+ }
+ })
+ }
+
+ $scope.makeReply = (topicNo) => {
+ console.log("sending new post to server")
+ let formData = new FormData()
+ formData.append('postContent', $scope.reply.postContent)
+ formData.append('topicNo', topicNo)
+ formData.append('userNo', stateData.state.user.userNo)
+ $http({
+ url : server + "/forum/topic/post/create",
+ method : "POST",
+ data : formData,
+ headers : { 'Content-Type' : undefined },
+ transformRequest : angular.identity
+ }).then((res) => {
+ if (res.status == 200) {
+ console.log("post added")
+ $scope.reply.postContent = ""
+ $timeout(function() { $scope.getTopic() }, 2000)
+ }
+ }).catch((res) => {
+ if (res.status == 500) {
+ console.log("something went wrong: ")
+ console.log(res.data)
+ $scope.status.text = "Something went wrong: " + res.status
+ }
+ })
+ }
+
+ $scope.setLogin = () => {
+ $location.path("/login")
+ }
+
+ $scope.setSignup = () => {
+ $location.path("/signup")
+ }
+
+ $scope.deletePost = (postNo) => {
+ if (!window.confirm("Are you sure you want to delete this post?")) {
+ return
+ }
+ $http({
+ url : server + "/forum/topic/post/delete?postNo=" + postNo,
+ method : "POST"
+ }).then((res) => {
+ if (res.status == 200) {
+ console.log("post deleted")
+ for (let i = 0; i < $scope.topic.posts.length; i++) {
+ if ($scope.topic.posts[i].postNo == postNo) {
+ $scope.topic.posts[i].postContent = "[deleted]"
+ }
+ }
+ }
+ }).catch((res) => {
+ if (res.status == 500) {
+ console.log("something went wrong: ")
+ console.log(res.data)
+ $scope.status.text = "Something went wrong: " + res.status
+ }
+ })
+ }
+
+ $scope.editPost = (postNo, postContent) => {
+ /* This is incomplete, need to figure out hiding the edit box after edit. Would rather not use timeout. */
+ console.log("submitting edited post")
+ let formData = new FormData()
+ formData.append("postNo", postNo)
+ formData.append("postContent", postContent)
+ $http({
+ url : server + "/forum/topic/post/edit",
+ method : "POST",
+ data : formData,
+ headers : { 'Content-Type' : undefined },
+ transformRequest : angular.identity
+ }).then((res) => {
+ if (res.status == 200) {
+ console.log("post edited")
+ for (let i = 0; i < $scope.topic.posts.length; i++) {
+ if ($scope.topic.posts[i].postNo == postNo) {
+ $scope.topic.posts[i].postContent = postContent + " [edited]"
+ }
+ }
+ }
+ }).catch((res) => {
+ if (res.status == 500) {
+ console.log("something went wrong: ")
+ console.log(res.data)
+ $scope.status.text = "Something went wrong: " + res.status
+ }
+ })
+ }
+
+})
+
+daddy.controller("topicCtrlr", function($scope, $http, $location, stateData) {
+
+ $scope.titles = {
+ heading : "Topics",
+ subtitle : "Most recent topics by category.",
+ list : "Topic",
+ last : "Category"
+ }
+
+ $scope.errors = {
+ error : {
+ show : false,
+ message : null
+ },
+ topic : {
+ show : false,
+ text : "None yet!"
+ }
+ }
+
+ $scope.getTopics = () => {
+ console.log("getting topics")
+ $http({
+ url : server + "/forum/topic/all",
+ method : "GET"
+ }).then((res) => {
+ if (res.status == 200) {
+ let topics = res.data
+ if (topics.length == 0) {
+ $scope.erros.topic.show = true
+ } else if (topics.length > 10) {
+ topics = topics.slice(0, 9)
+ }
+
+ for (let i = 0; i < topics.length; i++) {
+ topics[i].topicDate = niceDate(topics[i].topicDate)
+ }
+
+ $scope.topics = topics
+ }
+ }).catch((res) => {
+ if (res.status == 500) {
+ console.log(res.data)
+ $scope.errors.error.show = true
+ $scope.errors.error.start = "An error has occured: " + res.status
+ } else {
+ console.log(res)
+ }
+ })
+ }
+
+ $scope.setCat = (catNo) => {
+ $location.path("/categories/"+catNo)
+ }
+
+ $scope.setTopic = (topicNo) => {
+ $location.path("/topics/"+topicNo)
+ }
+
+})
+
+daddy.controller("categoriesCtrlr", function($scope, $http, $routeParams, $location, stateData) {
+
+ $scope.errors = {
+ exists : {
+ show : false,
+ text1 : "No such category,",
+ link1 : "Go Back!"
+ },
+ empty : {
+ show : false,
+ text : "No topics have been created yet..."
+ }
+ }
+
+ $scope.titles = {
+ topic : "topic",
+ date : "date"
+ }
+
+ $scope.getCat = () => {
+ console.log("getting category")
+ $http({
+ url : server + "/forum/category?cat=" + $routeParams.catNo,
+ method : "GET"
+ }).then((res) => {
+ console.log(res)
+ if (res.status == 200) {
+ let category = res.data
+ if (category.topics.length == 0) {
+ $scope.errors.empty.show = true
+ } else {
+ for (let i = 0; i < category.topics.length; i++) {
+ category.topics[i].topicDate = niceDate(category.topics[i].topicDate)
+ }
+ }
+ $scope.category = category
+ }
+ }).catch((res) => {
+ if (res.status == 404) {
+ console.log(res.data)
+ $scope.errors.exists.show = true
+ } else if (res.status == 500) {
+ console.log(res.data)
+ } else {
+ console.log(res)
+ }
+ })
+ }
+
+ $scope.setTopic = (topicNo) => {
+ $location.path("/topics/"+topicNo)
+ }
+
+ $scope.setForum = () => {
+ $location.path("/")
+ }
+})
+
+daddy.controller("categoryCtrlr", function($scope, $http, $location, stateData) {
+
+ $scope.titles = {
+ heading : "Categories",
+ subtitle : "Most recent categories and topics.",
+ list : "Catergory",
+ last : "Last Topic"
+ }
+
+ $scope.errors = {
+ topic : {
+ show : false,
+ text : "None yet!"
+ },
+ catagory : {
+ show : false,
+ text: "None yet!"
+ }
+ }
+
+ $scope.getCategories = () => {
+ console.log("getting categories form server")
+ $http({
+ url : server + "/forum/category/all",
+ method : "GET"
+ }).then((res) => {
+ if (res.status == 200) {
+ if (res.data.length == 0) {
+ $scope.errors.catagory.show = true
+ } else {
+ $scope.categories = res.data
+ for (let i = 0; i < res.data.length; i++) {
+ $http({
+ url : server + "/forum/category/topic/last?cat=" + res.data[i].catNo,
+ method : "GET"
+ }).then((res) => {
+ if (res.status == 200) {
+ console.log("topicNo: " + res.data.topicNo)
+ // if (res.data.topicNo == null) {
+ // $scope.errors.topic.show = true
+ // } else {
+ res.data.topicDate = niceDate(res.data.topicDate)
+ $scope.categories[i].lastTopic = res.data
+ // }
+ } else {
+ console.log("server has encountered an error: " + res.status)
+ }
+ })
+ }
+ console.log(res)
+ }
+ }
+ }).catch((res) => {
+ console.log("server has encountered an error: " + res.status)
+ })
+ }
+
+ $scope.setCat = (catNo) => {
+ $location.path("/categories/"+catNo)
+ }
+
+ $scope.setTopic = (topicNo) => {
+ $location.path("/topics/"+topicNo)
+ }
+})
+
+niceDate = (string) => {
+ let date = new Date(string)
+ return "on " + date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate() + " at " + date.getHours() + ":" + date.getMinutes()
+}
+
+isNum = (str) => {
+ let re = /^[0-9]+$/
+ return re.test(String(str).toLowerCase())
+}
\ No newline at end of file
diff --git a/scripts/main.js b/scripts/main.js
new file mode 100644
index 0000000..c0ecc5c
--- /dev/null
+++ b/scripts/main.js
@@ -0,0 +1,167 @@
+let daddy = angular.module("daddy", ["ngRoute", "ngAnimate", "ngCookies"])
+
+let server = "http://127.0.0.1:7001"
+
+daddy.controller("bodyCtrlr", function($scope, $cookies, stateData){
+
+ $scope.state = stateData.state
+
+ let user = $cookies.getObject('user')
+
+ if (user == null) {
+ stateData.logOut()
+ } else {
+ stateData.setUser(user)
+ }
+
+})
+
+daddy.controller("headerCtrlr", function($scope){
+ $scope.subtitle = "Inks forum."
+})
+
+daddy.controller("navCtrlr", function($scope, $location, $cookies, stateData){
+
+ $scope.setLogin = () => {
+ $location.path("/login")
+ }
+
+ $scope.setSignup = () => {
+ $location.path("/signup")
+ }
+
+ $scope.setForum = () => {
+ $location.path("/")
+ stateData.setPage('forum')
+ }
+
+ $scope.setTopics = () => {
+ $location.path("/topics")
+ stateData.setPage('topics')
+ }
+
+ $scope.setTopicCreate = () => {
+ $location.path("/topics/create")
+ stateData.setPage('topics')
+ }
+
+ $scope.setCategories = () => {
+ $location.path("/categories")
+ stateData.setPage('categories')
+ }
+
+ $scope.setCategoryCreate = () => {
+ $location.path("/categories/create")
+ stateData.setPage('categories')
+ }
+
+ switch ($location.path()) {
+ case "":
+ case "/":
+ $scope.setForum()
+ break
+ case "/topics":
+ $scope.setTopics()
+ // this has to be done in the controller
+ break
+ case "/topics/create":
+ $scope.setTopicCreate()
+ break
+ case "/categories":
+ $scope.setCategories()
+ // this has to be done in the controller
+ break
+ case "/catagories/create":
+ $scope.setCategoryCreate()
+ break
+ case "/login":
+ $scope.setLogin()
+ break
+ case "/signup":
+ $scope.setSignup()
+ break
+ }
+
+ $scope.logOut = () => {
+ $cookies.remove('user')
+ stateData.logOut()
+ }
+
+})
+
+// naming conventions here are very confusing:
+// the main topics page is topic and the specific topics page is topics, by specific I mean the :topicNo one.
+// the same goes for categories, category is the main page and the spcefics are categories.
+
+daddy.config(function ($routeProvider) {
+ $routeProvider.when("/", {templateUrl : "views/home.html", controller : "forumCtrlr"})
+ $routeProvider.when("/topics/create", {templateUrl : "views/create_topic.html", controller : "createTopicCtrlr"})
+ $routeProvider.when("/topics", {templateUrl : "views/topic.html", controller : "topicCtrlr"})
+ $routeProvider.when("/topics/:topicNo", {templateUrl : "views/topics.html", controller : "topicsCtrlr"})
+ $routeProvider.when("/categories/create", {templateUrl : "views/create_category.html", controller : "createCategoryCtrlr"})
+ $routeProvider.when("/categories", {templateUrl : "views/category.html", controller : "categoryCtrlr"})
+ $routeProvider.when("/categories/:catNo", {templateUrl : "views/categories.html", controller : "categoriesCtrlr"})
+ $routeProvider.when("/login", {templateUrl : "views/login.html", controller : "loginCtrlr"})
+ $routeProvider.when("/signup", {templateUrl : "views/signup.html", controller : "signupCtrlr"})
+ $routeProvider.otherwise({template : "404 ... no clue fam ... try something else? "})
+})
+
+daddy.factory('stateData', function(){
+ let state = {
+ page : "forum",
+ title : "Forum;",
+ user : {
+ userNo : null,
+ userName : null,
+ userLevel : 0
+ },
+ content : {
+ gallery : null
+ }
+ }
+
+ setPage = (newPage) => {
+ state.page = newPage
+ switch (state.page) {
+ case "forum":
+ state.title = "Forum;"
+ break
+ case "topics":
+ state.title = "Topics;"
+ break
+ case "categories":
+ state.title = "Categories;"
+ break
+ }
+ }
+
+ logOut = () => {
+ state.user.userNo = null
+ state.user.userName = null
+ state.user.userLevel = 0
+ }
+
+ setUser = (user) => {
+ state.user.userNo = user.userNo
+ state.user.userName = user.userName
+ state.user.userLevel = user.userLevel
+ }
+
+ return {
+ state : state,
+ setPage : setPage,
+ logOut: logOut,
+ setUser: setUser
+ }
+})
+
+function getDate() {
+ let date = new Date()
+ let out = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate() + " - " + date.getHours() + ":" + date.getMinutes()
+ return out
+}
+
+function validateEmail(email) {
+ var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+ return re.test(String(email).toLowerCase());
+}
\ No newline at end of file
diff --git a/server.js b/server.js
new file mode 100644
index 0000000..5410bf1
--- /dev/null
+++ b/server.js
@@ -0,0 +1,20 @@
+let express = require('express')
+let cors = require('cors')
+
+let app = express()
+
+app.use(express.static('./'))
+app.use(cors({
+ origin: "http://localhost:7001"
+}))
+
+app.get('/', (req, res) => {
+ res.sendFile('./index.html')
+})
+
+let port = 7000
+
+app.listen(port, () => {
+ console.log("Listening on port "+port+"...");
+ console.log("Go to: http://localhost:"+port+" or http://127.0.0.1:"+port+" to view...")
+})
\ No newline at end of file
diff --git a/styles/h5.css b/styles/h5.css
new file mode 100644
index 0000000..87ee684
--- /dev/null
+++ b/styles/h5.css
@@ -0,0 +1,211 @@
+/* General CSS for all pages */
+
+body{
+ position: relative;
+ width: 80%;
+ left: 10%;
+ background-color:#0F0F0F;
+ text-align: center; /* make sure IE centers the page too */
+ font-family: sans-serif;
+ color: lightgrey;
+}
+
+header{
+ text-align: left;
+ padding-left: 8%;
+}
+
+article{
+ margin-top: 5px;
+ border: 5px dashed pink;
+ border-top: none;
+ float: left;
+ padding: 20px 30px;
+ text-align: left;
+ width: 95%; /* fill up the entire div */
+ margin-bottom: 10px;
+}
+
+footer {
+ text-align: center;
+ font-size: 9pt;
+ color: #FAFAFA;
+}
+
+h1 {
+ font-size: 50pt;
+ margin-bottom: -25px;
+}
+
+h3 {
+ margin: 0;
+ padding: 0;
+}
+
+a:link, a:visited, a:hover, a:active{
+ text-decoration: none;
+ color: purple;
+}
+
+.status {
+ color: crimson;
+}
+
+.link {
+ color: pink;
+}
+
+.link:hover {
+ color: #009FC1;
+ text-decoration: underline;
+}
+
+.error {
+ text-align: center;
+}
+
+form {
+ display: inline-block;
+}
+
+input, label, textarea, select {
+ margin-bottom: 10px;
+}
+
+/* END Geteral CSS */
+
+/* CSS for menu on forum and gallery */
+nav {
+ margin-top: 2.5%;
+ float: left;
+ border: 5px dashed pink;
+ border-bottom: none; /* avoid a double border */
+ clear: both; /* clear:both makes sure the content div doesn't float next to this one but stays under it */
+ width:95%;
+ height:50px;
+ padding: 0 30px;
+ text-align: left;
+ font-size: 85%;
+}
+
+nav .mitem {
+ background-color: #2E2E2E;
+ border: 3px dashed pink;
+ float: left;
+ padding: 10px 0px 10px 0px;
+ text-decoration: none;
+ color: #FAFAFA;
+ margin-right: 10px;
+ margin-top: -25px;
+}
+
+nav .msub {
+ padding: 10px;
+ text-decoration: none;
+ color: #FAFAFA;
+ box-sizing: border-box;
+}
+
+nav span {
+ box-sizing: border-box;
+ padding: 10px;
+}
+
+nav .msub:hover {
+ background-color: #009FC1;
+}
+
+nav .msub:link, nav .msub:visited, nav .msub:active {
+ color: #FAFAFA;
+}
+
+nav .userbar {
+ float: right;
+}
+
+/* END menu CSS */
+
+/* CSS for things */
+.item{
+ display: inline-block;
+ position: relative;
+ margin: 50px;
+ margin-top: -10px;
+ text-align: center;
+}
+
+.block{
+ position: relative;
+ background: pink;
+}
+
+.blockTitle{
+ font-size: 20pt;
+ margin-bottom: -25px;
+}
+
+.CLICK{
+ font-size: 40pt;
+ padding-top: 8px;
+}
+/* END things CSS */
+
+.left {
+ display: block;
+ float: left;
+}
+
+.right {
+ display: block;
+ float: right;
+}
+
+.center {
+ display: block;
+ margin: auto;
+ width: min-content;
+ margin-bottom: 50px;
+}
+
+.border {
+ border: 3px dashed pink;
+ padding: 25px;
+}
+
+.stretch {
+ display: block;
+}
+
+/* START forum CSS */
+
+table {
+ text-align: left;
+ border-collapse: collapse;
+ width: 100%;
+}
+
+table, td, th {
+ border: 1px solid pink;
+}
+
+table a:hover {
+ color: pink;
+ text-decoration: none;
+}
+
+th {
+ background-color: #2E2E2E;
+ color: #FAFAFA;
+ padding: 5px;
+}
+
+td {
+ padding: 5px;
+}
+
+.right {
+ margin: 0 5px 0 10px;
+ float: right;
+}
+
+/* END forum CSS */
\ No newline at end of file
diff --git a/views/categories.html b/views/categories.html
new file mode 100644
index 0000000..1ebd439
--- /dev/null
+++ b/views/categories.html
@@ -0,0 +1,22 @@
+
+
+ {{category.catName}}
+ {{category.catDescr}}
+
+
+ {{titles.topic}}
+ {{titles.date}}
+
+
+ {{topic.topicSubject}}
+ {{topic.topicDate}}
+
+
+ {{errors.empty.text}}
+
+
+
+
+ {{errors.exists.text1}} {{errors.exists.link1}}
+
+
\ No newline at end of file
diff --git a/views/category.html b/views/category.html
new file mode 100644
index 0000000..7b67d0e
--- /dev/null
+++ b/views/category.html
@@ -0,0 +1,21 @@
+
+
+ {{titles.heading}}
+ {{titles.subtitle}}
+
+
+ {{titles.list}}
+ {{titles.last}}
+
+
+ {{cat.catName}}
+ {{cat.catDescr}}
+ {{cat.lastTopic.topicSubject}} {{cat.lastTopic.topicDate}}
+ {{errors.topic.text}}
+
+
+ {{errors.catagory.text}}
+
+
+
+
\ No newline at end of file
diff --git a/views/create_category.html b/views/create_category.html
new file mode 100644
index 0000000..3795acb
--- /dev/null
+++ b/views/create_category.html
@@ -0,0 +1,17 @@
+
+
+ {{titles.form}}
+
+ {{status.text}}
+
+
+ {{error.loggedIn.text1}} {{error.loggedIn.link1}} {{error.loggedIn.text2}}
+ {{error.userLevel.text1}}
+
+
\ No newline at end of file
diff --git a/views/create_topic.html b/views/create_topic.html
new file mode 100644
index 0000000..4c1443d
--- /dev/null
+++ b/views/create_topic.html
@@ -0,0 +1,23 @@
+
+
+
+ {{error.loggedIn.text1}} {{error.loggedIn.link1}} {{error.loggedIn.text2}}
+
+
\ No newline at end of file
diff --git a/views/footer.html b/views/footer.html
new file mode 100644
index 0000000..3139320
--- /dev/null
+++ b/views/footer.html
@@ -0,0 +1 @@
+Copyright © inkletblot.com 2018 and forever.
\ No newline at end of file
diff --git a/views/header.html b/views/header.html
new file mode 100644
index 0000000..95f8755
--- /dev/null
+++ b/views/header.html
@@ -0,0 +1,4 @@
+{{state.title}}
+{{subtitle}}
+
\ No newline at end of file
diff --git a/views/home.html b/views/home.html
new file mode 100644
index 0000000..9986835
--- /dev/null
+++ b/views/home.html
@@ -0,0 +1,25 @@
+
+
+
+ {{titles.main.heading}}
+
+
+
+ {{titles.recent.heading}}
+
+
+ {{titles.recent.list}}
+ {{titles.recent.last}}
+
+
+ {{cat.catName}}
+ {{cat.catDescr}}
+ {{cat.lastTopic.topicSubject}} {{cat.lastTopic.topicDate}}
+ {{errors.topic.text}}
+
+
+ {{errors.catagory.text}}
+
+
+
+
\ No newline at end of file
diff --git a/views/login.html b/views/login.html
new file mode 100644
index 0000000..f90cf88
--- /dev/null
+++ b/views/login.html
@@ -0,0 +1,17 @@
+
+
+ {{titles.signup}}
+ {{message.start}} {{message.link}} {{message.end}}
+
+
+
\ No newline at end of file
diff --git a/views/nav.html b/views/nav.html
new file mode 100644
index 0000000..04c3f0d
--- /dev/null
+++ b/views/nav.html
@@ -0,0 +1,26 @@
+
+ Forum
+
+
+
+ Categories >
+ Create a Category
+
+
+
+ Topics >
+ Create a Topic
+
+
+
+
+ Welcome {{state.user.userName}}!
+ Log Out
+
+
+ Log in
+
+
+ Sign Up
+
+
\ No newline at end of file
diff --git a/views/signup.html b/views/signup.html
new file mode 100644
index 0000000..3bc8c96
--- /dev/null
+++ b/views/signup.html
@@ -0,0 +1,23 @@
+
+
+
+ {{titles.success}}
+ {{message.start}} {{message.line2}} {{message.link}} {{message.end}}
+
+
\ No newline at end of file
diff --git a/views/topic.html b/views/topic.html
new file mode 100644
index 0000000..27c9566
--- /dev/null
+++ b/views/topic.html
@@ -0,0 +1,20 @@
+
+
+ {{titles.heading}}
+ {{titles.subtitle}}
+
+
+ {{titles.list}}
+ {{titles.last}}
+
+
+ {{topic.topicSubject}} {{topic.topicDate}}
+ {{topic.topicCat.catName}}
+
+
+ {{errors.topic.text}}
+
+
+ {{errors.error.message}}
+
+
\ No newline at end of file
diff --git a/views/topics.html b/views/topics.html
new file mode 100644
index 0000000..7b79c5c
--- /dev/null
+++ b/views/topics.html
@@ -0,0 +1,47 @@
+
+
+
+ {{message.start}} {{message.link}}
+
+
+