diff --git a/firmware/__init__.py b/firmware/__init__.py
index 448cf55..db4c900 100644
--- a/firmware/__init__.py
+++ b/firmware/__init__.py
@@ -1,16 +1,18 @@
-"""Firmware module"""
+"""Firmware module first run at app init.Defines the database name"""
import os
from flask import Flask, request, session, g, redirect, url_for
from flask import abort, render_template, flash
from flask.ext.bcrypt import Bcrypt
+from flask_wtf.csrf import CSRFProtect
app = Flask(__name__)
app.config.from_object(__name__)
app.config.update(dict(
DATABASE=os.path.join(app.root_path, 'firmware.db'),
- SECRET_KEY='key'
+ SECRET_KEY='key',
+ TRAP_BAD_REQUEST_ERRORS=True
))
encrypt = Bcrypt(app)
@@ -24,3 +26,4 @@
from firmware import widgets
from firmware import decorators
from firmware import authorization
+from firmware import helper_functions
diff --git a/firmware/authorization.py b/firmware/authorization.py
index c60ee09..d315190 100644
--- a/firmware/authorization.py
+++ b/firmware/authorization.py
@@ -1,5 +1,6 @@
""" module for operations that need authorization """
+
def authorize_edit_company(session, company):
""" function that verifies if the user in the current session has editing
rights over the company """
diff --git a/firmware/decorators.py b/firmware/decorators.py
index 97dfbbb..35ce246 100644
--- a/firmware/decorators.py
+++ b/firmware/decorators.py
@@ -1,12 +1,12 @@
""" module for the decorators in firmware """
-
+import json
from functools import wraps
from flask import render_template
from firmware.views import session
-def login_required(role):
+def login_required(role, method="GET"):
"""function that calls the view decorator with 1 parameter"""
def view_decorator(funct):
""" the actual deocrator that wraps a view function and renders it only
@@ -21,10 +21,15 @@ def check_login(*args, **kwargs):
if session is None:
errors = {'no_server': "Server not running at the moment!"}
return render_template('404.html', error_messages=errors)
- if session.get('user', None) is None and role == 'user':
- errors = {'not_logged': "Your account must be of type '%s' to \
- continue" % (role)}
- return render_template('404.html', error_messages=errors)
+ if session.get('user', None) is None:
+ errors = {'not_logged': "You must be logged in to continue"}
+ if method == "GET":
+ return render_template('404.html', error_messages=errors)
+ response = {
+ 'success': False,
+ 'errors': errors
+ }
+ return json.dumps(response)
if role == 'admin' and session['user'].get('privilege', 'user') == 'user':
errors = {'not_logged_admin': "You must be logged in as an admin \
to access this page.Please upgrade to admin"}
diff --git a/firmware/forms.py b/firmware/forms.py
index 1fc99fa..0650bb2 100644
--- a/firmware/forms.py
+++ b/firmware/forms.py
@@ -5,6 +5,7 @@
PasswordField, SelectField
from firmware.widgets import WidgetTextArea, WidgetPassword, WidgetRadio, \
WidgetSubmit, WidgetFile, WidgetSelect
+from firmware import repository
class AddUser(Form):
@@ -22,8 +23,8 @@ class AddUser(Form):
password = PasswordField(
'password',
[
- validators.InputRequired(message='Please add a password'),
- validators.DataRequired(message='Please add a password'),
+ validators.InputRequired(message='Input add a password'),
+ validators.DataRequired(message='Passwords must match, Data'),
validators.EqualTo('confirm_password',
message='Passwords must match'),
validators.Length(min=2, max=120, message='Password must be between\
@@ -32,9 +33,9 @@ class AddUser(Form):
widget=WidgetPassword(maxlength=120)
)
confirm_password = PasswordField(
- 'confirm password',
+ 'confirm_password',
[
- validators.InputRequired(message='Please confirm your password!'),
+ validators.InputRequired(message='Please confirm your password!--inputccccc'),
],
widget=WidgetPassword(maxlength=120)
)
@@ -82,15 +83,12 @@ class AddUser(Form):
)
privilege = RadioField(
'privilege',
- [
- validators.InputRequired()
- ],
widget=WidgetRadio(),
choices=[('user', 'user'), ('admin', 'admin')], default='user'
)
avatar = FileField(
'avatar',
- widget=WidgetFile()
+ widget=WidgetFile(id="picture")
)
submit = SubmitField(
'ADD USER',
@@ -149,8 +147,11 @@ class AddCompany(Form):
category_id = SelectField(
label='category',
coerce=int,
- choices=[('default', 'add some items')],
- widget=WidgetSelect(disabled="--SELECT A CATEGORY--")
+ choices=[(a.id, a.domain) for a in repository.get_categories()],
+ widget=WidgetSelect(disabled="--SELECT A CATEGORY--"),
+ validators=[
+ validators.Required(message="Please select a category")
+ ]
)
details = TextAreaField(
'details',
@@ -164,7 +165,7 @@ class AddCompany(Form):
)
logo = FileField(
'logo',
- widget=WidgetFile()
+ widget=WidgetFile(id="picture")
)
submit = SubmitField(
'ADD COMPANY',
@@ -172,13 +173,6 @@ class AddCompany(Form):
)
- def __init__(self, *args, **kwargs):
- super(AddCompany, self).__init__(*args, **kwargs)
- if kwargs.get('categories', None) is not None:
- self.category_id.choices = [(a.id, a.domain) \
- for a in kwargs.get('categories')]
-
-
def to_dict(self):
""" returns the data in the form fields to a mutable dictionary
format """
@@ -225,7 +219,7 @@ class AddReview(Form):
message = TextAreaField(
'review',
[
- validators.DataRequired(message="Please write a review"),
+ validators.InputRequired(message="Please write a review"),
validators.Length(min=2, max=2000, message="Review must be \
max. 2000 characters")
],
diff --git a/firmware/helper_functions.py b/firmware/helper_functions.py
new file mode 100644
index 0000000..368688e
--- /dev/null
+++ b/firmware/helper_functions.py
@@ -0,0 +1,15 @@
+""" module containing functions unrelated to the main modules of the app
+ used to execute specific tasks """
+
+from werkzeug.datastructures import MultiDict
+
+
+def dict_to_multidict(dictionary):
+ """ transforms a dictionary, dict, to a MultiDict and returns the
+ MultiDict """
+ if not dictionary:
+ return MultiDict()
+ imd = MultiDict()
+ for key, value in dictionary.items():
+ imd.add(key, value)
+ return imd
diff --git a/firmware/models.py b/firmware/models.py
index fd441d0..5f5a9a6 100644
--- a/firmware/models.py
+++ b/firmware/models.py
@@ -47,6 +47,19 @@ def serialize(self):
'privilege': self.privilege,
'gender': self.gender
}
+ def to_dict(self):
+ """returns the user object as a dictionary"""
+ return {
+ 'username': self.username,
+ 'email': self.email,
+ 'name': self.name,
+ 'surname': self.surname,
+ 'avatar': self.avatar,
+ 'contact': self.contact,
+ 'privilege': self.privilege,
+ 'gender': self.gender,
+ 'password': self.password
+ }
class Review(Base):
@@ -66,6 +79,16 @@ def __init__(self, **kwargs):
self.company_id = kwargs.get('company_id', None)
self.rating = kwargs.get('rating', None)
+ def to_dict(self):
+ """ returns the review object data as a dictionary """
+ return {
+ 'user_id': self.user_id,
+ 'message': self.message,
+ 'company_id': self.company_id,
+ 'rating': self.rating
+ }
+
+
class Company(Base):
"""defines / maps companies table in database"""
__tablename__ = 'companies'
@@ -95,6 +118,7 @@ def __init__(self, **kwargs):
def to_dict(self):
"""returns the data in the model object to a mutable dictionary form"""
return {
+ 'id': self.id,
'name': self.name,
'description': self.description,
'details': self.details,
diff --git a/firmware/static/Avatars/Rares.jpg b/firmware/static/Avatars/Rares.jpg
index 27761d7..699beb1 100644
Binary files a/firmware/static/Avatars/Rares.jpg and b/firmware/static/Avatars/Rares.jpg differ
diff --git a/firmware/static/js/add-company.js b/firmware/static/js/add-company.js
new file mode 100644
index 0000000..f27c8c4
--- /dev/null
+++ b/firmware/static/js/add-company.js
@@ -0,0 +1,21 @@
+function addCompany(response_data, user_data) {
+ form_locked = document.getElementsByClassName("add-company-layout")[0];
+ form_locked.style.display = "none";
+ if(response_data.flash == "add") {
+ flashMessage("Please wait a moment while we add "+response_data.name+" to our list");
+ }
+ else {
+ flashMessage("Please wait a moment while we update your company");
+ }
+ setTimeout(function() {
+ window.location.replace("http://0.0.0.0:5000/details/"+response_data.id);
+ }, 3700);
+ form_locked.style.visibility = "flex";
+}
+
+document.getElementById('category_id').addEventListener("change", function() {
+ var categories = document.getElementById('category_id');
+ categories.options[categories.options.selectedIndex].selected = true;
+});
+
+submitForm("add-company", addCompany, flash_errors);
diff --git a/firmware/static/js/add-review.js b/firmware/static/js/add-review.js
new file mode 100644
index 0000000..6b10aa1
--- /dev/null
+++ b/firmware/static/js/add-review.js
@@ -0,0 +1,40 @@
+function addReview(response_data, user_data) {
+
+ var review_list = document.getElementById("review-list");
+ var list_item = document.createElement('li');
+ list_item.setAttribute("class", "details-reviews");
+
+ var username_and_avatar = document.createElement('div');
+ username_and_avatar.setAttribute("class", "username-avatar");
+
+ var username = document.createElement('h2');
+ username.setAttribute("class", "details-user");
+ username.innerHTML = user_data.username;
+
+ var avatar = document.createElement("img");
+ avatar.setAttribute("class", "review-avatar");
+ if(user_data.avatar !== "default.jpg") {
+ avatar.setAttribute("src", "../static/Avatars/"+user_data.avatar);
+ }
+ else {
+ avatar.setAttribute("src", "../static/Defaults/user/"+user_data.avatar);
+ }
+
+ var review_message = document.createElement("p");
+ review_message.setAttribute("class", "details-user-review");
+ review_message.innerHTML = response_data.message;
+
+ review_list.insertBefore(list_item, review_list.firstChild);
+ list_item.appendChild(username_and_avatar);
+ list_item.appendChild(review_message);
+ username_and_avatar.appendChild(username);
+ username_and_avatar.appendChild(avatar);
+
+ review_list.scrollTop = 0;
+ document.getElementById("title-reviews").scrollIntoView();
+ document.getElementById('message').value = "";
+
+ flashMessage("Review added sucessfully");
+}
+
+submitForm("reviews-bottom", addReview, flash_errors);
diff --git a/firmware/static/js/add-user.js b/firmware/static/js/add-user.js
new file mode 100644
index 0000000..7f09012
--- /dev/null
+++ b/firmware/static/js/add-user.js
@@ -0,0 +1,8 @@
+function addUser(response_data, user_data) {
+ flashMessage("New user, "+response_data.username+", added sucessfully");
+ setTimeout(function() {
+ window.location.replace("http://0.0.0.0:5000/home#reload-point");
+ }, 1700);
+}
+
+submitForm("add-user", addUser, flash_errors);
diff --git a/firmware/static/js/flash-message.js b/firmware/static/js/flash-message.js
new file mode 100644
index 0000000..db9b398
--- /dev/null
+++ b/firmware/static/js/flash-message.js
@@ -0,0 +1,26 @@
+function flashMessage(message) {
+
+ var alerts = document.getElementById("alerts");
+ if (alerts == null) {
+ alerts = document.createElement('div');
+ alerts.setAttribute("class", "alerts");
+ alerts.setAttribute("id", "alerts");
+ }
+ document.getElementsByClassName('header')[0].appendChild(alerts);
+ var alert_body = document.createElement('div');
+ alert_body.setAttribute("class", "alert-flash");
+ alert_body.innerHTML = message;
+
+
+ var alert_close = document.createElement('span');
+ alert_close.setAttribute("class", "closebtn");
+ alert_close.setAttribute("onclick", "this.parentElement.style.display='none';");
+ alert_close.innerHTML="×";
+
+ alert_body.appendChild(alert_close);
+ alerts.appendChild(alert_body);
+
+ setTimeout(function() {
+ fade_out(alert_body);
+ }, 2700);
+}
diff --git a/firmware/static/js/helper-functions.js b/firmware/static/js/helper-functions.js
new file mode 100644
index 0000000..e95da0b
--- /dev/null
+++ b/firmware/static/js/helper-functions.js
@@ -0,0 +1,53 @@
+function fade_out(element) {
+ var op = 1;
+ var timer = setInterval(function () {
+ if (op <= 0.05) {
+ clearInterval(timer);
+ element.style.display = 'none';
+ element.parentNode.removeChild(element);
+ }
+ element.style.opacity = op;
+ element.style.filter = 'alpha(opacity=' + op * 100 + ")";
+ op -= op * 0.05;
+ }, 5);
+}
+
+
+function image_to_viewport(vieportId, input, callback) {
+ var canvas = document.getElementById(vieportId);
+ context = canvas.getContext('2d');
+ if(input.files && input.files[0]) {
+ var fr = new FileReader();
+ fr.onload = function(e) {
+ var img = new Image();
+ img.addEventListener("load", function() {
+ canvas.width = img.width;
+ canvas.height = img.height;
+ context.drawImage(img, 0, 0);
+ base64image = getBase64Image(canvas);
+ callback(base64image);
+ });
+ img.src = e.target.result;
+ };
+ fr.readAsDataURL( input.files[0] );
+ }
+}
+
+
+function getBase64Image(canvas) {
+ if(canvas)
+ {
+ dataURL = canvas.toDataURL('image/jpeg');
+ return dataURL;
+ }
+ return "NO CANVAS ERROR";
+}
+
+
+if(document.getElementById('category_id') != null) {
+ document.getElementById('category_id').addEventListener("change", function(){
+ var categories = document.getElementById('category_id');
+ categories.options[categories.options.selectedIndex].selected = true;
+ console.log("done somethign");
+ });
+}
diff --git a/firmware/static/js/login.js b/firmware/static/js/login.js
new file mode 100644
index 0000000..d8d63df
--- /dev/null
+++ b/firmware/static/js/login.js
@@ -0,0 +1,12 @@
+function loginSuccess(response_data, user_data) {
+ form_locked = document.getElementsByClassName("login-data")[0];
+ button_locked = document.getElementById("log-button");
+ button_locked.style.display = "none";
+ form_locked.style.display = "none";
+ flashMessage("Welcome back, dear "+response_data.username+".Glad to see you again!");
+ setTimeout(function() {
+ window.location.replace("http://0.0.0.0:5000/home#reload-point");
+ }, 2000);
+}
+
+submitForm("login-data", loginSuccess, flash_errors);
diff --git a/firmware/static/js/submit-form-errors.js b/firmware/static/js/submit-form-errors.js
new file mode 100644
index 0000000..7a93bfb
--- /dev/null
+++ b/firmware/static/js/submit-form-errors.js
@@ -0,0 +1,33 @@
+var okToAlert = true;
+function flash_errors(errors){
+ if(okToAlert)
+ {
+ var alerts = document.getElementById("alerts");
+ if (alerts == null) {
+ alerts = document.createElement('div');
+ alerts.setAttribute("class", "alerts");
+ alerts.setAttribute("id", "alerts");
+ }
+ document.getElementsByClassName('header')[0].appendChild(alerts);
+ for(var error in errors) {
+ if(errors.hasOwnProperty(error)) {
+ var alert_body = document.createElement('div');
+ alert_body.setAttribute("class", "alert");
+ alert_body.innerHTML = errors[error];
+
+ var alert_close = document.createElement('span');
+ alert_close.setAttribute("class", "closebtn");
+ alert_close.setAttribute("onclick", "this.parentElement.style.display='none';");
+ alert_close.innerHTML="×";
+
+ alert_body.appendChild(alert_close);
+ alerts.appendChild(alert_body);
+ }
+ }
+ okToAlert = false;
+ setTimeout(function(){
+ fade_out(alerts);
+ setTimeout(function(){ okToAlert=true; }, 300);
+ }, 1500);
+ }
+}
diff --git a/firmware/static/js/submit-form.js b/firmware/static/js/submit-form.js
new file mode 100644
index 0000000..77d484b
--- /dev/null
+++ b/firmware/static/js/submit-form.js
@@ -0,0 +1,55 @@
+var base64Image;
+form_data = {};
+
+function submitForm(selector, successHandler, errorsHandler) {
+ var form = document.getElementsByClassName(selector)[0];
+ form.addEventListener('submit', function(ev) {
+ ev.preventDefault();
+
+ textareas = form.getElementsByTagName('textarea');
+ for (var i=0; i
- {% else %}
- - {{ review.message|safe }} -
-
+ {% else %}
+ + {{ review.message|safe }} +
+
{% else %}
-