diff --git a/tetawebapp/config.local.py b/tetawebapp/config.local.py index 4d2898d..49c93c7 100644 --- a/tetawebapp/config.local.py +++ b/tetawebapp/config.local.py @@ -1,2 +1,3 @@ SQLALCHEMY_TRACK_MODIFICATIONS = True SQLALCHEMY_DATABASE_URI = "postgresql://tetawebapp:tetawebapp@localhost/tetawebapp" +UPLOADED_FILES_DEST = "./upload" diff --git a/tetawebapp/static/images/upload.png b/tetawebapp/static/images/upload.png new file mode 100644 index 0000000..0c4969a Binary files /dev/null and b/tetawebapp/static/images/upload.png differ diff --git a/tetawebapp/static/scripts/tetawebapp.js b/tetawebapp/static/scripts/tetawebapp.js index d1e95b5..17f8404 100644 --- a/tetawebapp/static/scripts/tetawebapp.js +++ b/tetawebapp/static/scripts/tetawebapp.js @@ -4,6 +4,9 @@ var light_red = "#FCD5DC"; var light_green = "#D5FCD8"; var base_bg = "#FFFFFF"; var base_border = "#888888"; +var coloured_bg = "#FF5D00"; +var clear_bg = "#E5E5E5"; +var text_color = "#555555"; /* ************************************************************************************** * GLOBAL @@ -26,6 +29,17 @@ function getcookie(cname) { } // Eye candies +function valid_input(obj) { + // Valid input makes obj background to glow green for 2 seconds + // If obj borders were red, they get they normal color back + obj.style.backgroundColor = light_green; + obj.style.borderColor = base_border; + setTimeout( function() { + obj.style.backgroundColor = base_bg; + } + , 2000); +} + function invalid_input(obj) { // Invalid input makes obj borders and background to glow red for 2 seconds // Border color will stay red until a valid input is sent @@ -37,17 +51,48 @@ function invalid_input(obj) { , 2000); } -function valid_input(obj) { +function valid_upload(obj) { // Valid input makes obj background to glow green for 2 seconds // If obj borders were red, they get they normal color back - obj.style.backgroundColor = light_green; - obj.style.borderColor = base_border; + obj.style.backgroundColor = green; + obj.style.borderColor = text_color; + obj.style.borderStyle = 'solid'; setTimeout( function() { - obj.style.backgroundColor = base_bg; + obj.style.backgroundColor = clear_bg; + obj.style.borderStyle = 'none'; } , 2000); } +function invalid_upload(obj) { + // Invalid input makes obj borders and background to glow red for 2 seconds + // Border color will stay red until a valid input is sent + obj.style.backgroundColor = red; + obj.style.borderColor = text_color; + obj.style.borderStyle = 'solid'; + setTimeout( function() { + obj.style.borderStyle = 'solid'; + obj.style.backgroundColor = clear_bg; + obj.style.borderColor = red; + } + , 2000); +} + +function lit(obj) { + // Lit bacground and border on obj (use by input type=file) + obj.style.backgroundColor = coloured_bg; + obj.style.borderColor = text_color; + obj.style.borderStyle = 'solid'; +} + +function unlit(obj) { + // Unlit bacground and border on obj (use by input type=file) + obj.style.backgroundColor = clear_bg; + obj.style.borderColor = clear_bg; + obj.style.borderStyle = 'none'; +} + + function verify_login() { // Verify login inputs login = document.getElementById('login'); @@ -140,7 +185,7 @@ function get_value_from_ajax(obj, url, err_code) { xhttp.onload = function(){ if (xhttp.status != 200) { invalid_input(obj); - } + } }; xhttp.onreadystatechange = function() { @@ -156,6 +201,44 @@ function get_value_from_ajax(obj, url, err_code) { } }; xhttp.open('POST', url, true); + alert(xhttp.readyState); xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); xhttp.send(); } + +function upload_file_from_ajax(obj, url, err_code) { + // Upload files get from to the specified + // if is returned input is invalidated + var files = obj.files; + var icon_id = obj.id.substring(obj.id.lastIndexOf("_") + 1); + var icon_obj = document.getElementById("upload_icon_" + icon_id) + var xhttp = new XMLHttpRequest(); + xhttp.onerror = function(){ + invalid_upload(icon_obj); + }; + + xhttp.onload = function(){ + if (xhttp.status != 200) { + invalid_upload(icon_obj); + } + }; + + xhttp.onreadystatechange = function() { + if (xhttp.readyState == 4 && xhttp.status == 200) { + var response = xhttp.responseText; + if (response == err_code) { + invalid_upload(icon_obj); + return; + } + valid_upload(icon_obj); + return; + } + }; + + xhttp.open('POST', url, true); + var formData = new FormData(); + for (var i=0; i < files.length; i++){ + formData.append("files", files[i], files[i].name); + } + xhttp.send(formData); +} diff --git a/tetawebapp/static/styles/colors.css b/tetawebapp/static/styles/colors.css index 361cac1..59daf9d 100644 --- a/tetawebapp/static/styles/colors.css +++ b/tetawebapp/static/styles/colors.css @@ -25,4 +25,5 @@ --save_icon: url(/static/images/save.png); --search_icon: url(/static/images/search.png); --trash_icon: url(/static/images/trash.png); + --upload_icon: url(/static/images/upload.png); } diff --git a/tetawebapp/static/styles/tetawebapp.css b/tetawebapp/static/styles/tetawebapp.css index 62dc348..fca02ad 100644 --- a/tetawebapp/static/styles/tetawebapp.css +++ b/tetawebapp/static/styles/tetawebapp.css @@ -254,12 +254,36 @@ input[type="submit"] { button:hover, input[type="button"]:hover, -input[type="submit"]:hover { +input[type="submit"]:hover, +input[type="file"]:hover { background-color: var(--light-coloured-bg); color: var(--text-color); cursor: pointer; } +div.file_upload { + display: inline-block; + position: relative; + width: 20px; + height: 20px; + margin: 0; + padding: 0; + border-radius: 2px; + border-style: solid; + border-width: 1px; + border-color: var(--clear-bg); +} + +input[type="file"] { + position: absolute; + width: 18px; + height: 18px; + left: 0; + top: 1px; + opacity: 0; +} + + input.add, input.edit, input.login, @@ -267,7 +291,8 @@ input.logout, input.refresh, input.save, input.search, -input.trash { +input.trash, +input.upload { width: 20px; height: 20px; margin: 0; @@ -283,7 +308,8 @@ input.logout:hover, input.refresh:hover, input.save:hover, input.search:hover, -input.trash:hover { +input.trash:hover, +input.upload:hover { border-color: var(--text-color); border-style: solid; border-width: 1px; @@ -331,3 +357,8 @@ input.trash { background-repeat: no-repeat; background-position: center center; } +input.upload { + background: var(--upload_icon); + background-repeat: no-repeat; + background-position: center center; +} diff --git a/tetawebapp/templates/ajax.html b/tetawebapp/templates/ajax.html index 81c2625..c80118c 100644 --- a/tetawebapp/templates/ajax.html +++ b/tetawebapp/templates/ajax.html @@ -25,4 +25,16 @@ +
+

Upload files with AJAX

+

Select files to upload

+

The response may randomly be an error response so you should try it more than once.

+
+ + + +
+
{% endblock %} diff --git a/tetawebapp/templates/inputs.html b/tetawebapp/templates/inputs.html index 24fee74..cb9a8c8 100644 --- a/tetawebapp/templates/inputs.html +++ b/tetawebapp/templates/inputs.html @@ -20,14 +20,21 @@

- - - - - - - - + + + + + + + + + +
+ + +

 #!/bin/sh
diff --git a/tetawebapp/tetawebapp.py b/tetawebapp/tetawebapp.py
index 0e2d40e..4b24874 100755
--- a/tetawebapp/tetawebapp.py
+++ b/tetawebapp/tetawebapp.py
@@ -224,7 +224,7 @@ def get_html_from_ajax():
   """ Return HTML code to an AJAX request
       It may generate a 404 http error for testing purpose """
   if int(random.random()*10) % 2:
-    # Randomlu generate 404 HTTP response
+    # Randomly generate 404 HTTP response
     return render_template('error.html'), 404
   return render_template('ajax_html.html')
 
@@ -236,7 +236,7 @@ def get_value_from_ajax():
   err_code = 'TETA_ERR'
   RND = int(random.random()*10)
   if RND % 2:
-    # Randomlu generate error
+    # Randomly generate error
     return err_code
   return str(RND)
 
@@ -250,6 +250,31 @@ def set_value_from_ajax(value):
     return 'True'
   return err_code
 
+@app.route("/upload", methods=['POST'])
+@check_session
+def upload():
+  """ Accept a value from an AJAX request
+      It may return an error code for testing purpose """
+  err_code = 'TETA_ERR'
+  RND = int(random.random()*10)
+  if RND % 2:
+    # Randomly generate error
+    print err_code
+    return err_code
+  uploaded_files = []
+  if len(request.files) > 0 and request.files['files']:
+    uploaded_files = request.files.getlist("files")
+  print "Uploaded files:"
+  for f in uploaded_files:
+    print '  [+] %s [%s]' % (f.filename, f.content_type)
+    # Befor saving you should:
+    # - Secure the filename
+    # - Check file size
+    # - Check content type
+    f.save(os.path.join(app.config['UPLOADED_FILES_DEST'], f.filename))
+    f.close()
+  return "OK"
+
 ########################################################################
 # Main
 ########################################################################
diff --git a/tetawebapp/upload/what_you_ride_final.xcf b/tetawebapp/upload/what_you_ride_final.xcf
new file mode 100644
index 0000000..1b33a18
Binary files /dev/null and b/tetawebapp/upload/what_you_ride_final.xcf differ
diff --git a/tetawebapp/upload/what_you_ride_final2.png b/tetawebapp/upload/what_you_ride_final2.png
new file mode 100644
index 0000000..f5ea54e
Binary files /dev/null and b/tetawebapp/upload/what_you_ride_final2.png differ
diff --git a/tetawebapp/upload/what_you_ride_final2.xcf b/tetawebapp/upload/what_you_ride_final2.xcf
new file mode 100644
index 0000000..6f90714
Binary files /dev/null and b/tetawebapp/upload/what_you_ride_final2.xcf differ
diff --git a/tetawebapp/upload/what_you_ride_final3.png b/tetawebapp/upload/what_you_ride_final3.png
new file mode 100644
index 0000000..18ade61
Binary files /dev/null and b/tetawebapp/upload/what_you_ride_final3.png differ
diff --git a/tetawebapp/upload/what_you_ride_final3.xcf b/tetawebapp/upload/what_you_ride_final3.xcf
new file mode 100644
index 0000000..ae307fa
Binary files /dev/null and b/tetawebapp/upload/what_you_ride_final3.xcf differ