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

- THE REQUESTED PAGE WAS NOT FOUND! + THE REQUESTED PAGE WAS NOT FOUND!

{% for key, value in error_messages.items() %}

diff --git a/firmware/templates/_formhelpers.html b/firmware/templates/_formhelpers.html index 0274c7d..6418ff0 100644 --- a/firmware/templates/_formhelpers.html +++ b/firmware/templates/_formhelpers.html @@ -10,10 +10,10 @@ {% macro render_radio_field(field) %}

{{ field.label.text }}
{% for subfield in field %} -
- {{ subfield(**kwargs)|safe }} - {{ subfield.label }} -
+
+ {{ subfield(**kwargs)|safe }} + {{ subfield.label }} +
{% endfor %} {% endmacro %} diff --git a/firmware/templates/add_company.html b/firmware/templates/add_company.html index 471c790..e38cb3c 100644 --- a/firmware/templates/add_company.html +++ b/firmware/templates/add_company.html @@ -5,7 +5,7 @@ {% from "_formhelpers.html" import render_submit_field %} {% from "_formhelpers.html" import render_select_field %}
-
+
{{ render_text_field(add_company_form.name,rows='100',cols='50') }} @@ -24,6 +24,7 @@
{{ render_text_field(add_company_form.logo) }} + {{ render_submit_field(add_company_form.submit) }}
@@ -31,5 +32,11 @@
{% endblock %} +{% block js %} + {{ super() }} + +{% endblock %} + + {% block footer %} {% endblock %} diff --git a/firmware/templates/add_user.html b/firmware/templates/add_user.html index 20164ec..3ae881b 100644 --- a/firmware/templates/add_user.html +++ b/firmware/templates/add_user.html @@ -5,41 +5,42 @@ {% from "_formhelpers.html" import render_radio_field %} {% from "_formhelpers.html" import render_submit_field %}
- +
{{ render_text_field(add_user_form.username) }}
- {{ render_text_field(add_user_form.password) }} + {{ render_text_field(add_user_form.password) }}
- {{ render_text_field(add_user_form.confirm_password) }} - + {{ render_text_field(add_user_form.confirm_password) }} +
- {{ render_text_field(add_user_form.email) }} + {{ render_text_field(add_user_form.email) }}
- {{ render_text_field(add_user_form.name) }} + {{ render_text_field(add_user_form.name) }}
- {{ render_text_field(add_user_form.surname) }} + {{ render_text_field(add_user_form.surname) }}
- {{ render_text_field(add_user_form.contact) }} + {{ render_text_field(add_user_form.contact) }}
- {{ render_radio_field(add_user_form.gender) }} + {{ render_radio_field(add_user_form.gender) }}
- {{ render_radio_field(add_user_form.privilege) }} + {{ render_radio_field(add_user_form.privilege) }}
{{ render_text_field(add_user_form.avatar) }} + {{ render_submit_field(add_user_form.submit) }}
@@ -48,3 +49,8 @@ {% block footer %} {% endblock %} + +{% block js %} + {{ super() }} + +{% endblock %} diff --git a/firmware/templates/details.html b/firmware/templates/details.html index 1a1dc64..da153f8 100644 --- a/firmware/templates/details.html +++ b/firmware/templates/details.html @@ -59,42 +59,45 @@

-
-
-

REVIEWS

-
    - {% for review in reviews %} -
  • -
    -

    - {{ review.user.username }} -

    -
    - {% if review.user.avatar == "default.jpg" %} - - {% else %} - - {% endif %} -
    -
    -

    - {{ review.message|safe }} -

    -
  • - {% else %} -
  • NO REVIEWS YET
  • - {% endfor %} -
+
+
+

REVIEWS

+
    + {% for review in reviews %} +
  • +
    +

    + {{ review.user.username }} +

    + {% if review.user.avatar == "default.jpg" %} + + {% else %} + + {% endif %} +
    +

    + {{ review.message|safe }} +

    +
  • + {% else %} +
  • NO REVIEWS YET
  • + {% endfor %} +
- {{ render_text_field(add_review_form.message)}} - {{ render_submit_field(add_review_form.submit)}} + {{ render_text_field(add_review_form.message)}} + {{ render_submit_field(add_review_form.submit)}}
{% endblock %} +{% block js %} + {{ super() }} + +{% endblock %} + {% block footer %} {% endblock %} diff --git a/firmware/templates/layout.html b/firmware/templates/layout.html index ee67261..96294a9 100644 --- a/firmware/templates/layout.html +++ b/firmware/templates/layout.html @@ -78,17 +78,17 @@

THE BIGGEST SERVICE FINDER ONLINE

{% if session['user']['avatar'] == "default.jpg" %} {% else %} - + {% endif %} - {% else %} - {% endif %} @@ -108,7 +108,7 @@

THE BIGGEST SERVICE FINDER ONLINE

-
+
{% endblock %} @@ -152,5 +152,12 @@

THE BIGGEST SERVICE FINDER ONLINE

» {% endblock %} + + {% block js %} + + + + + {% endblock %} diff --git a/firmware/templates/login.html b/firmware/templates/login.html index 92135e6..9b72354 100644 --- a/firmware/templates/login.html +++ b/firmware/templates/login.html @@ -3,11 +3,11 @@ {% block content %} {% from "_formhelpers.html" import render_text_field %} {% from "_formhelpers.html" import render_submit_field %} -
+
-
- {{ render_text_field(login_form.username) }} -
+
+ {{ render_text_field(login_form.username) }} +
{{ render_text_field(login_form.password) }}
@@ -18,5 +18,10 @@ {% endblock %} +{% block js %} + {{ super() }} + +{% endblock %} + {% block footer %} {% endblock %} diff --git a/firmware/uploaders.py b/firmware/uploaders.py index 636ff4b..fced086 100644 --- a/firmware/uploaders.py +++ b/firmware/uploaders.py @@ -1,15 +1,15 @@ -"""UPLOADERS - function to upload files locally""" +"""UPLOADERS - function to upload files to server""" import os +import base64 -def upload_file(new_file, upload_name, upload_type): - """ uploads a file to the server """ - filename = "default.jpg" - if new_file: - filename_temp = upload_name + ".jpg" - filename = new_file.filename - folder = os.path.join(os.path.dirname(__file__), 'static', upload_type) - new_file.save(os.path.join(folder, filename)) - os.rename(os.path.join(folder, filename), os.path.join(folder, filename_temp)) - filename = filename_temp - return filename + +def upload_base64_file(info, filename, foldername): + """ decodes an image from base64 format to File """ + if not info: + return "default.jpg" + folder = os.path.join(os.path.dirname(__file__), 'static', foldername) + imgdata = base64.b64decode(info.split(",")[1]) + with open(folder+"/"+filename+".jpg", 'wb') as new_file: + new_file.write(imgdata) + return filename+".jpg" diff --git a/firmware/views.py b/firmware/views.py index 6641a0f..7df6d73 100644 --- a/firmware/views.py +++ b/firmware/views.py @@ -1,19 +1,18 @@ # coding: utf8 """ Routes for app paths and functions combining multiple modules """ - +import json import sqlalchemy.orm -from flask import request, session, redirect, url_for, render_template, flash +from flask import request, session, render_template, redirect from firmware import app from firmware import repository from firmware.models import Company, User, Review -from firmware.uploaders import upload_file +from firmware.uploaders import upload_base64_file from firmware.forms import AddUser, AddCompany, LogIn, AddReview from firmware.decorators import login_required from firmware.authorization import authorize_edit_company from firmware import encrypt - - +from firmware.helper_functions import dict_to_multidict def get_details(company_id): @@ -64,18 +63,29 @@ def details(company_id): category=category, added_by_user=added_by_user) -@app.route('/details/', methods=['POST']) -@login_required('user') +@app.route('/add-review/', methods=['POST']) +@login_required('user', method='POST') def add_review(company_id): """ function to add a review when user clicks submit """ - add_review_form = AddReview(request.form) + review_data = dict_to_multidict(request.json) + add_review_form = AddReview(review_data) if add_review_form.validate(): review = Review(user_id=session['user']['id'], company_id=company_id, rating=0, **add_review_form.to_dict()) repository.add_review(review) - flash("Review added sucessfully") - return redirect(url_for('details', company_id=company_id)) + response = { + 'success': True, + 'data': review.to_dict(), + 'user': session.get('user', "no-user-error") + } + return json.dumps(response) + response = { + 'success': False, + 'errors': add_review_form.errors + } + return json.dumps(response) + @app.route('/company/', defaults={'company_id': None}, methods=['GET', 'POST']) @app.route('/company/', methods=['GET', 'POST']) @@ -83,103 +93,171 @@ def add_review(company_id): def add_company(company_id): """ function called when an admin wants to add a new company or edit \ one of it's own """ - categories = repository.get_categories() - if company_id and request.method == 'GET': + if company_id: try: company = repository.get_company(company_id) - errors = authorize_edit_company(session, company) - if errors: - return render_template('404.html', error_messages=errors) add_new_company_form = AddCompany(obj=company, categories=categories) add_new_company_form.submit.label.text = "SAVE CHANGES" + errors = authorize_edit_company(session, company) + if errors: + return render_template('404.html', error_messages=errors) except sqlalchemy.orm.exc.NoResultFound: errors = dict() errors['404'] = "Company does not exist!" return render_template('404.html', error_messages=errors) else: - add_new_company_form = AddCompany(request.form, categories=categories) - - if request.method == 'POST' and add_new_company_form.validate(): - if company_id is None: - logo = upload_file(request.files['logo'], - add_new_company_form.name.data, - "Images") - add_new_company_form.logo.data = logo - company = Company(added_by_id=session['user']['id'], - **add_new_company_form.to_dict()) - new_company_id = repository.add_company(company).id - flash('Congratulations on adding your company, ' \ - + company.name +' to our website!Check out \ - your profile below.') - return redirect(url_for('details', - company_id=new_company_id)) - updated_company_id = repository.update_company(request.form.to_dict(), - company_id).id - flash('Your company has been updated!') - return redirect(url_for('details', - company_id=updated_company_id)) - + add_new_company_form = AddCompany(categories=categories) return render_template('add_company.html', categories=categories, company_id=company_id, add_company_form=add_new_company_form, errors=add_new_company_form.errors) +@app.route('/company/add/', defaults={'company_id': None}, methods=['POST']) +@app.route('/company/add/', methods=['POST']) +@login_required('admin', method='POST') +def add_new_company(company_id): + """ route for AJAX post request from client + adds a company to the database if request data is valid""" + company_data = dict_to_multidict(request.json) + add_new_company_form = AddCompany(company_data) + if add_new_company_form.validate(): + logo = upload_base64_file(request.json.get('picture', None), + request.json.get('name'), + 'Images') + add_new_company_form.logo.data = logo + if company_id is None: + company = Company(added_by_id=session['user']['id'], + **add_new_company_form.to_dict()) + added_company = repository.add_company(company) + added_company_dict = added_company.to_dict() + added_company_dict['flash'] = "add" + del added_company_dict['added_by_user'] + response = { + 'success': True, + 'data': added_company_dict, + 'user': session.get('user', "no-user-error") + } + return json.dumps(response) + updated_company = repository.update_company(add_new_company_form.to_dict(), + company_id) + updated_company_dict = updated_company.to_dict() + updated_company_dict['flash'] = "update" + del updated_company_dict['added_by_user'] + response = { + 'success': True, + 'data': updated_company_dict, + 'user': session.get('user', "no-user-error") + } + return json.dumps(response) + response = { + 'success': False, + 'errors': add_new_company_form.errors + } + return json.dumps(response) -@app.route('/add/user/', methods=['GET', 'POST']) +@app.route('/user/new', methods=['GET', 'POST']) @login_required('admin') def add_user(): """ function called ONLY when an ADMIN wants to add a new user """ - add_user_form = AddUser(request.form) - if request.method == 'POST' and add_user_form.validate(): - add_user_form.password.data = encrypt.generate_password_hash( - add_user_form.password.data - ) - user = User(avatar=upload_file(request.files['avatar'], - add_user_form.username.data, - "Avatars"), - **add_user_form.to_dict()) - new_user = repository.add_user(user) - # print str(new_user_id) + "will be used when adding a new user page" - flash("NEW USER, %s, ADDED SUCESFULLY!" % new_user.username) - return redirect(url_for('home', current_category=\ - request.args.get('category', \ - 'all categories'))) - return render_template('add_user.html', - errors=add_user_form.errors, - add_user_form=add_user_form) + add_user_form = AddUser() + return render_template('add_user.html', add_user_form=add_user_form) + @app.route('/login', methods=['GET', 'POST']) def login(): - """ function called when an anon user wants to log in""" - errors = dict() - login_form = LogIn(request.form) - if request.method == 'POST' and login_form.validate(): + """ view to render the form for logging in """ + login_form = LogIn() + if session.get('user', None) is not None: + errors = dict() + errors['user'] = "You are already logged in.Please log out before \ + trying to log in again" + return render_template('404.html', error_messages=errors) + return render_template('login.html', data=request.form, + session=session, login_form=login_form) + + +@app.route('/login/request', methods=['POST']) +def login_request(): + """ route for the AJAX post request for logging in + logs a user in if data is valid """ + login_data = dict_to_multidict(request.json) + login_form = LogIn(login_data) + if session.get('user', None) is not None: + errors = { + 'already-logged': "An account is already logged in" + } + response = { + 'success': False, + 'errors': errors + } + if login_form.validate(): if repository.check_user(login_form.username.data, login_form.password.data) is False: - errors['invalid'] = "Invalid credentials!Please check your username \ - and password again!" - else: - user = repository.get_user(login_form.username.data).serialize() - session['logged_in'] = True - session['user'] = user - flash('Welcome back, dear %s!' % user['username']) - return render_template('index.html', user=user, - companies=repository.get_companies(), - categories=repository.get_categories()) - - errors.update(login_form.errors) - return render_template('login.html', errors=errors, data=request.form, - session=session, login_form=login_form) + errors = { + 'invalid': "Invalid credentials!Please check your username and \ + password again!" + } + response = { + 'success': False, + 'errors': errors + } + return json.dumps(response) + user = repository.get_user(login_form.username.data).serialize() + session['logged_in'] = True + session['user'] = user + response = { + 'success': True, + 'data': user, + 'user': user + } + return json.dumps(response) + response = { + 'success': False, + 'errors': login_form.errors + } + return json.dumps(response) + + +@app.route('/user/add', methods=['POST']) +@login_required('admin', method='POST') +def add_new_user(): + """ route for the AJAX post request + adds a user to database if data from the request is valid """ + new_user_data = dict_to_multidict(request.json) + add_user_form = AddUser(new_user_data) + if add_user_form.validate(): + add_user_form.password.data = encrypt.generate_password_hash( + add_user_form.password.data + ) + user = User(avatar=upload_base64_file(request.json.get('picture', None), + request.json['username'], + "Avatars"), + **add_user_form.to_dict()) + new_user = repository.add_user(user) + response = { + 'success': True, + 'data': new_user.to_dict(), + 'user': session.get('user', "no-user-error") + } + return json.dumps(response) + response = { + 'success': False, + 'errors': add_user_form.errors + } + return json.dumps(response) -@app.route('/logout') +@app.route('/logout', methods=['POST', 'GET']) def logout(): """ route acessed when log-out button is clicked """ + errors = dict() session['logged_in'] = False - session.pop('user', None) - flash('You were logged out') - return redirect(url_for('home')) + if session.get('user', None) is None: + errors['not_logged'] = "You must be logged in to log out" + return render_template("404.html", error_messages=errors) + session.pop('user') + return redirect("/home#reload-point") diff --git a/firmware/widgets.py b/firmware/widgets.py index c456724..e8492bb 100644 --- a/firmware/widgets.py +++ b/firmware/widgets.py @@ -3,8 +3,6 @@ -TextAreaField, SelextField, FileField, PasswordField, RadioField SubmitField """ - - from wtforms.widgets import PasswordInput, TextArea, FileInput, HTMLString, \ Select, html_params, SubmitInput, ListWidget @@ -19,7 +17,7 @@ def __init__(self, **kwargs): self.properties = { 'class': kwargs.get('_class', 'add-user --input-new-user'), 'placeholder': kwargs.get('placeholder', 'Enter your info'), - 'onfocus': kwargs.get('onfocus', "this.placeholder = None"), + 'onfocus': kwargs.get('onfocus', "this.placeholder = ''"), 'onblur': kwargs.get('onblur', "this.placeholder = 'Enter your info\ '"), } @@ -31,10 +29,13 @@ def __init__(self, **kwargs): self.properties['rows'] = str(kwargs.get('rows', "20")) if kwargs.get('cols', None) is not None: self.properties['cols'] = str(kwargs.get('cols', "80")) + if kwargs.get('id', None) is not None: + self.properties['id'] = str(kwargs.get('id', "no-id")) def __call__(self, field, **kwargs): self.properties['placeholder'] = "Enter " + field.label.text - self.properties['onblur'] = "Enter " + field.label.text + self.properties['onblur'] = "this.placeholder = 'Enter " \ + + field.label.text +"'" return super(WidgetTextArea, self).__call__(field, **self.properties) @@ -46,10 +47,13 @@ def __init__(self, **kwargs): self.properties = { 'class': kwargs.get('_class', 'file-upload') } + if kwargs.get('id', None) is not None: + self.properties['id'] = str(kwargs.get('id', "no-id")) def __call__(self, field, **kwargs): return super(WidgetFile, self).__call__(field, **self.properties) + class WidgetPassword(PasswordInput): """ adds extra properties toa Password INPUT: - _class - adds a class (same as WidgetTextArea)"""