diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4119916
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+data
+data.db
diff --git a/Dockerfile b/Dockerfile
index 3f2c87f..befc2f4 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -6,6 +6,8 @@ COPY src /blog
 
 WORKDIR /blog
 
+VOLUME /blog/data
+
 RUN pip3 install -r requirements.txt
 
 VOLUME /blog/templates/entry
diff --git a/README.md b/README.md
index 482789c..b41a5a3 100644
--- a/README.md
+++ b/README.md
@@ -6,21 +6,21 @@ The blog is intended to be used to review and critique things.
 ## Features/To-Dos
 
 
-- [ ] Accounts
+- [x] Accounts
   - [x] Login
   - [x] Logout
-  - [ ] Register
+  - [x] Register
 - [ ] Review blog entries
-  - [ ] Writing entries
+  - [x] Writing entries
   - [ ] Editing entries
   - [ ] Deleting entries
 - [ ] Infinite-scroll blog page
-- [ ] Archive page
-  - [ ] Months as headings
-  - [ ] Links to scrolling blog page
-  - [ ] Links to standalone article
-- [ ] Standalone article page
-  - [ ] Links to scrolling blog page
+- [x] Archive page
+  - [x] Months as headings
+  - [x] Links to scrolling blog page
+  - [x] Links to standalone article
+- [x] Standalone article page
+  - [x] Links to scrolling blog page
 - [ ] RSS feed
 - [ ] Eye candy
   - [ ] Star rating
@@ -58,9 +58,9 @@ Set the following volumes with the -v tag.
 
 | Volume-Name   | Container mount        | Description                        |
 | ------------- | ---------------------- | ---------------------------------- |
-| `config-file` | `/blog/src/config.py`  | Config file                        |
-| `css`         | `/blog/src/static/css` | (optional) Directory for css files |
-| `html`        | `/blog/src/templates`  | (optional) Directory for templates |
+| `config-file` | `/blog/config.py`  | Config file                        |
+| `data`        | `/blog/data`       | Directory for data                 |
+| `css`         | `/blog/static/css` | (optional) Directory for css files |
 
 #### Ports
 
diff --git a/rebuild.sh b/rebuild.sh
index 816ac62..24c20a1 100755
--- a/rebuild.sh
+++ b/rebuild.sh
@@ -6,4 +6,5 @@ docker run --name container-critique \
     --restart unless-stopped \
     -p "5000:5000" \
     -e FLASK_ENV=development \
+    -v data:/blog/data
     -d tiyn/container-critique
diff --git a/src/app.py b/src/app.py
index ab3ad17..2c3bf94 100644
--- a/src/app.py
+++ b/src/app.py
@@ -1,14 +1,21 @@
-from flask import Flask, flash, make_response, render_template, request, redirect, abort, url_for
-from flask_login import current_user, login_user, LoginManager, logout_user
+from flask import Flask, flash, make_response, render_template, redirect, \
+    abort, url_for
+from flask_login import current_user, login_user, LoginManager, logout_user, \
+    login_required
 from flask_wtf import CSRFProtect
+import os
 
-import content as con_gen
 import config
+import content as con_gen
+from database import Database, User
+from forms import LoginForm, RegisterForm, WriteForm
 
 
 app = Flask(__name__)
 csrf = CSRFProtect()
-app.secret_key = "123534"
+db = Database()
+
+app.secret_key = os.urandom(32)
 csrf.init_app(app)
 
 login = LoginManager(app)
@@ -18,6 +25,8 @@ TITLE = config.TITLE
 STYLE = config.STYLE
 DESCRIPTION = config.DESCRIPTION
 WEBSITE = config.WEBSITE
+REGISTER = config.REGISTER
+
 
 @app.errorhandler(404)
 def page_not_found(e):
@@ -38,9 +47,9 @@ def archive():
     return render_template("archive.html", title=TITLE, content_string=content, style=STYLE)
 
 
-@app.route("/entry/" + year + "
\n"
+            content_string += "\n"
+            last_year = year
+        content_string += "
"
+        content_string += "" + title + " (" + year + ") - " + \
+            str(rating) + "/100
\n"
+        content_string += "[" + "standalone" + "]
\n"
+        content_string += text
+        content_string += "
"
+        content_string += "" + \
+            str(reviewed) + " by " + username + ""
+        content_string += "" + title + \
+            " (" + year + ") - " + str(rating) + "/100 
\n"
+        content_string += "["
+        content_string += "" + "link" + ""
+        content_string += "]
\n"
+        content_string += "" + \
+            str(reviewed) + " by " + username + ""
+        content_string += "
\n"
+        content_string += text
+        content_string += "
"
+    return content_string
 
 
 def get_rss_string():
diff --git a/src/data.db b/src/data.db
deleted file mode 100644
index ae25c7d..0000000
Binary files a/src/data.db and /dev/null differ
diff --git a/src/data/.gitkeep b/src/data/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/src/database.py b/src/database.py
index 6ef3a3a..cded5fa 100644
--- a/src/database.py
+++ b/src/database.py
@@ -1,14 +1,37 @@
+from datetime import date as dt
 import os
 import sqlite3
+from werkzeug.security import generate_password_hash, check_password_hash
 
-from login import User
+class User():
+
+    def __init__(self, name, pass_hash=None):
+        self.name = name
+        self.id = 0
+        self.is_active = True
+        self.is_authenticated = True
+        self.is_anonymous = False
+        self.pass_hash = pass_hash
+
+    def set_password(self, password):
+        self.pass_hash = generate_password_hash(password)
+
+    def set_id(self, ident):
+        self.id = ident
+
+    def check_password(self, password):
+        return check_password_hash(self.pass_hash, password)
+
+    def get_id(self):
+        return self.id
 
 
 class Database:
 
     def __init__(self):
-        self.TABLE_FILE = 'USERS'
-        self.DB_DIR = os.path.dirname(".")
+        self.USER_TABLE_FILE = 'USERS'
+        self.ENTRY_TABLE_FILE = 'ENTRIES'
+        self.DB_DIR = os.path.dirname("./data/")
         self.setup_db()
 
     def connect(self):
@@ -22,41 +45,78 @@ class Database:
         """Creates a database with tables."""
         db = self.connect()
         crs = db.cursor()
-        query = "CREATE TABLE IF NOT EXISTS " + self.TABLE_FILE + \
+        query = "CREATE TABLE IF NOT EXISTS " + self.USER_TABLE_FILE + \
             "(id INTEGER PRIMARY KEY AUTOINCREMENT," + \
             "name CHAR(32) NOT NULL UNIQUE," + \
             "password CHAR(32) NOT NULL)"
         crs.execute(query)
+        query = "CREATE TABLE IF NOT EXISTS " + self.ENTRY_TABLE_FILE + \
+            "(id INTEGER PRIMARY KEY AUTOINCREMENT," + \
+            "name CHAR(64) NOT NULL," + \
+            "date CHAR(4) NOT NULL," + \
+            "text TEXT NOT NULL," + \
+            "rating INTEGER NOT NULL," +\
+            "user_id INTEGER," +\
+            "reviewed CHAR(10) NOT NULL," +\
+            "FOREIGN KEY(user_id) REFERENCES " + self.USER_TABLE_FILE + "(id))"
+        crs.execute(query)
+        db.commit()
 
-    def insert_user(self, name, password):
+    def insert_user(self, user):
         """Insert a new user into the database.
         """
-        if self.check_name(name):
+        if self.check_user_name(user.name) and user.pass_hash is not None:
             db = self.connect()
             crs = db.cursor()
-            query = "INSERT INTO " + self.TABLE_FILE + "(`name`,`password`)" + \
+            query = "INSERT INTO " + self.USER_TABLE_FILE + "(`name`,`password`)" + \
                     "VALUES (?, ?) ON CONFLICT DO NOTHING"
-            crs.execute(query, (name, password))
+            crs.execute(query, (user.name, user.pass_hash))
             db.commit()
-            return True
-        return False
+            return crs.lastrowid
+        return None
 
-    def check_name(self, name):
-        if self.get_by_name(name) is None:
-            return True
-        return False
-
-    def get_by_id(self, ident):
+    def insert_entry(self, name, date, text, rating, user_id=None):
+        """Insert a new user into the database.
+        """
         db = self.connect()
         crs = db.cursor()
-        query = "SELECT * FROM " + self.TABLE_FILE + " WHERE id = ?"
+        reviewed = dt.today().strftime('%Y-%m-%d')
+        query = "INSERT INTO " + self.ENTRY_TABLE_FILE + "(`name`,`date`, `text`, `rating`, `user_id`, `reviewed`)" + \
+                "VALUES (?, ?, ?, ?, ?, ?)"
+        crs.execute(query, (name, date, text, rating, user_id, reviewed))
+        db.commit()
+        return crs.lastrowid
+
+    def get_entries(self):
+        db = self.connect()
+        crs = db.cursor()
+        query = "SELECT * FROM " + self.ENTRY_TABLE_FILE
+        crs.execute(query)
+        return crs.fetchall()
+
+    def check_user_name(self, name):
+        if self.get_user_by_name(name) is None:
+            return True
+        return False
+
+    def get_entry_by_id(self, ident):
+        db = self.connect()
+        crs = db.cursor()
+        query = "SELECT * FROM " + self.ENTRY_TABLE_FILE + " WHERE id = ?"
         crs.execute(query, (ident, ))
         return crs.fetchone()
 
-    def get_by_name(self, name):
+    def get_user_by_id(self, ident):
         db = self.connect()
         crs = db.cursor()
-        query = "SELECT * FROM " + self.TABLE_FILE + " WHERE name = ?"
+        query = "SELECT * FROM " + self.USER_TABLE_FILE + " WHERE id = ?"
+        crs.execute(query, (ident, ))
+        return crs.fetchone()
+
+    def get_user_by_name(self, name):
+        db = self.connect()
+        crs = db.cursor()
+        query = "SELECT * FROM " + self.USER_TABLE_FILE + " WHERE name = ?"
         crs.execute(query, (name, ))
         return crs.fetchone()
 
@@ -64,3 +124,9 @@ class Database:
         user = User(name, pass_hash)
         user.set_id(ident)
         return user
+
+
+#db = Database()
+#db.insert_entry("name", "2020", "text", 50, 1)
+#res = db.get_entries()
+#print(res)
diff --git a/src/forms.py b/src/forms.py
new file mode 100644
index 0000000..d551894
--- /dev/null
+++ b/src/forms.py
@@ -0,0 +1,26 @@
+from datetime import date
+from flask_wtf import FlaskForm
+from wtforms import StringField, PasswordField, SubmitField, TextAreaField
+from wtforms.fields.html5 import IntegerField
+from wtforms.validators import DataRequired, EqualTo, InputRequired, NumberRange
+
+
+class LoginForm(FlaskForm):
+    username = StringField("Username", validators=[DataRequired()])
+    password = PasswordField("Password", validators=[DataRequired()])
+    submit = SubmitField("Sign In")
+
+
+class RegisterForm(FlaskForm):
+    username = StringField("Username", validators=[DataRequired()])
+    password = PasswordField("Password", validators=[DataRequired()])
+    password2 = PasswordField(
+        "Repeat Password", validators=[DataRequired(), EqualTo("password")])
+    submit = SubmitField("Register")
+
+class WriteForm(FlaskForm):
+    name = StringField("Name", validators=[DataRequired()])
+    date = IntegerField("Release Year", default=date.today().year, validators=[DataRequired(), NumberRange(min=0, max=date.today().year, message="Year has to be valid.")])
+    text = TextAreaField("Text", validators=[DataRequired()])
+    rating = IntegerField("Rating", default=50, validators=[InputRequired(), NumberRange(min=0, max=100, message="Number has to be between 0 and 100.")])
+    submit = SubmitField("Publish")
diff --git a/src/login.py b/src/login.py
deleted file mode 100644
index c7220c5..0000000
--- a/src/login.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from flask_wtf import FlaskForm
-from wtforms import StringField, PasswordField, SubmitField, BooleanField
-from wtforms.validators import DataRequired
-
-from werkzeug.security import generate_password_hash, check_password_hash
-
-
-class User():
-
-    def __init__(self, name, pass_hash=None):
-        self.name = name
-        self.id = 0
-        self.is_active = True
-        self.is_authenticated = True
-        self.is_anonymous = False
-        self.pass_hash = pass_hash
-
-    def set_password(self, password):
-        self.pass_hash = generate_password_hash(password)
-
-    def set_id(self, ident):
-        self.id = ident
-
-    def check_password(self, password):
-        return check_password_hash(self.pass_hash, password)
-
-    def get_id(self):
-        return self.id
-
-class LoginForm(FlaskForm):
-    username = StringField("Username", validators=[DataRequired()])
-    password = PasswordField("Password", validators=[DataRequired()])
-    remember_me = BooleanField("Remember Me")
-    submit = SubmitField("Sign In")
diff --git a/src/requirements.txt b/src/requirements.txt
index 120c115..b311835 100644
--- a/src/requirements.txt
+++ b/src/requirements.txt
@@ -1,5 +1,6 @@
 Flask==2.1.2
 Flask_Login==0.6.2
 Flask_WTF==0.14.3
-Werkzeug==2.1.2
+Werkzeug==2.0.0
 WTForms==2.2.1
+jinja2==3.0.3
diff --git a/src/templates/login.html b/src/templates/login.html
index 0c1f14c..5358fc6 100644
--- a/src/templates/login.html
+++ b/src/templates/login.html
@@ -15,7 +15,6 @@
                 {{ form.password.label }}
                 {{ form.password(size=32) }}
             
{{ form.remember_me() }} {{ form.remember_me.label }}
{{ form.submit() }}
{% for mesg in get_flashed_messages() %}{{ mesg }}
diff --git a/src/templates/register.html b/src/templates/register.html new file mode 100644 index 0000000..e864fbe --- /dev/null +++ b/src/templates/register.html @@ -0,0 +1,38 @@ +{% extends "template.html" %} + +{% block content %} + +