Mercurial > public > sg101
changeset 722:71d17d267e27
Added an ajax form to upload photos & update post box w/image code.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Sat, 21 Sep 2013 17:00:49 -0500 |
parents | 378b55b81de3 |
children | c0199d3d3b86 |
files | forums/forms.py sg101/templates/forums/show_form.html static/js/jquery.form.min.js static/js/markitup/sets/markdown/set.js user_photos/urls.py user_photos/views.py |
diffstat | 6 files changed, 143 insertions(+), 25 deletions(-) [+] |
line wrap: on
line diff
--- a/forums/forms.py Thu Sep 19 20:00:10 2013 -0500 +++ b/forums/forms.py Sat Sep 21 17:00:49 2013 -0500 @@ -13,6 +13,17 @@ from forums.signals import notify_new_topic, notify_new_post +FORUMS_FORM_CSS = { + 'all': (settings.GPP_THIRD_PARTY_CSS['markitup'] + + settings.GPP_THIRD_PARTY_CSS['jquery-ui']) +} +FORUMS_FORM_JS = ( + settings.GPP_THIRD_PARTY_JS['markitup'] + + settings.GPP_THIRD_PARTY_JS['jquery-ui'] + + ['js/jquery.form.min.js', 'js/forums.js'] +) + + class NewPostForm(forms.Form): """Form for creating a new post.""" body = forms.CharField(label='', @@ -22,13 +33,8 @@ topic = None class Media: - css = { - 'all': (settings.GPP_THIRD_PARTY_CSS['markitup'] + - settings.GPP_THIRD_PARTY_CSS['jquery-ui']), - } - js = (settings.GPP_THIRD_PARTY_JS['markitup'] + - settings.GPP_THIRD_PARTY_JS['jquery-ui'] + - ['js/forums.js']) + css = FORUMS_FORM_CSS + js = FORUMS_FORM_JS def __init__(self, *args, **kwargs): super(NewPostForm, self).__init__(*args, **kwargs) @@ -76,13 +82,8 @@ has_mod_fields = False class Media: - css = { - 'all': (settings.GPP_THIRD_PARTY_CSS['markitup'] + - settings.GPP_THIRD_PARTY_CSS['jquery-ui']), - } - js = (settings.GPP_THIRD_PARTY_JS['markitup'] + - settings.GPP_THIRD_PARTY_JS['jquery-ui'] + - ['js/forums.js']) + css = FORUMS_FORM_CSS + js = FORUMS_FORM_JS def __init__(self, user, forum, *args, **kwargs): super(NewTopicForm, self).__init__(*args, **kwargs) @@ -153,13 +154,8 @@ fields = ('body', ) class Media: - css = { - 'all': (settings.GPP_THIRD_PARTY_CSS['markitup'] + - settings.GPP_THIRD_PARTY_CSS['jquery-ui']), - } - js = (settings.GPP_THIRD_PARTY_JS['markitup'] + - settings.GPP_THIRD_PARTY_JS['jquery-ui'] + - ['js/forums.js']) + css = FORUMS_FORM_CSS + js = FORUMS_FORM_JS def __init__(self, *args, **kwargs): topic_name = kwargs.pop('topic_name', None)
--- a/sg101/templates/forums/show_form.html Thu Sep 19 20:00:10 2013 -0500 +++ b/sg101/templates/forums/show_form.html Sat Sep 21 17:00:49 2013 -0500 @@ -32,8 +32,29 @@ <br /> <fieldset> <legend>Video Attachments</legend> + <p><a href="/forums/topic/18843/">Need help?</a></p> <div id="attachment"></div> </fieldset> </fieldset> </form> + +<form id="photo-upload-form" action="{% url 'user_photos-upload_ajax' %}" method="post" + enctype="multipart/form-data">{% csrf_token %} +<fieldset> + <legend>Upload Photo</legend> + <p> + You can upload a photo directly from your computer or device with this + form. After the photo is uploaded it will be resized and an image code will be + placed in the post box, above. + </p> + <p>To hot-link a photo hosted elsewhere, use the + <img src="{{ STATIC_URL }}js/markitup/sets/markdown/images/picture.png"> + icon on the post box. + </p> + <input type="file" name="image_file" /><br /> + <input type="submit" id="photo-upload-submit" name="submit" value="Upload photo" /> + <div style="width: 80%;margin:auto;"><div id="photo-upload-progress" + style="margin:auto;width:100%;"></div></div> +</fieldset> +</form>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/static/js/jquery.form.min.js Sat Sep 21 17:00:49 2013 -0500 @@ -0,0 +1,7 @@ +/* +* jQuery Form Plugin; v20130916 +* http://jquery.malsup.com/form/ +* Copyright (c) 2013 M. Alsup; Dual licensed: MIT/GPL +* https://github.com/malsup/form#copyright-and-license +*/ +;(function(e){"use strict";function t(t){var r=t.data;t.isDefaultPrevented()||(t.preventDefault(),e(t.target).ajaxSubmit(r))}function r(t){var r=t.target,a=e(r);if(!a.is("[type=submit],[type=image]")){var n=a.closest("[type=submit]");if(0===n.length)return;r=n[0]}var i=this;if(i.clk=r,"image"==r.type)if(void 0!==t.offsetX)i.clk_x=t.offsetX,i.clk_y=t.offsetY;else if("function"==typeof e.fn.offset){var o=a.offset();i.clk_x=t.pageX-o.left,i.clk_y=t.pageY-o.top}else i.clk_x=t.pageX-r.offsetLeft,i.clk_y=t.pageY-r.offsetTop;setTimeout(function(){i.clk=i.clk_x=i.clk_y=null},100)}function a(){if(e.fn.ajaxSubmit.debug){var t="[jquery.form] "+Array.prototype.join.call(arguments,"");window.console&&window.console.log?window.console.log(t):window.opera&&window.opera.postError&&window.opera.postError(t)}}var n={};n.fileapi=void 0!==e("<input type='file'/>").get(0).files,n.formdata=void 0!==window.FormData;var i=!!e.fn.prop;e.fn.attr2=function(){if(!i)return this.attr.apply(this,arguments);var e=this.prop.apply(this,arguments);return e&&e.jquery||"string"==typeof e?e:this.attr.apply(this,arguments)},e.fn.ajaxSubmit=function(t){function r(r){var a,n,i=e.param(r,t.traditional).split("&"),o=i.length,s=[];for(a=0;o>a;a++)i[a]=i[a].replace(/\+/g," "),n=i[a].split("="),s.push([decodeURIComponent(n[0]),decodeURIComponent(n[1])]);return s}function o(a){for(var n=new FormData,i=0;a.length>i;i++)n.append(a[i].name,a[i].value);if(t.extraData){var o=r(t.extraData);for(i=0;o.length>i;i++)o[i]&&n.append(o[i][0],o[i][1])}t.data=null;var s=e.extend(!0,{},e.ajaxSettings,t,{contentType:!1,processData:!1,cache:!1,type:u||"POST"});t.uploadProgress&&(s.xhr=function(){var r=e.ajaxSettings.xhr();return r.upload&&r.upload.addEventListener("progress",function(e){var r=0,a=e.loaded||e.position,n=e.total;e.lengthComputable&&(r=Math.ceil(100*(a/n))),t.uploadProgress(e,a,n,r)},!1),r}),s.data=null;var l=s.beforeSend;return s.beforeSend=function(e,t){t.data=n,l&&l.call(this,e,t)},e.ajax(s)}function s(r){function n(e){var t=null;try{e.contentWindow&&(t=e.contentWindow.document)}catch(r){a("cannot get iframe.contentWindow document: "+r)}if(t)return t;try{t=e.contentDocument?e.contentDocument:e.document}catch(r){a("cannot get iframe.contentDocument: "+r),t=e.document}return t}function o(){function t(){try{var e=n(g).readyState;a("state = "+e),e&&"uninitialized"==e.toLowerCase()&&setTimeout(t,50)}catch(r){a("Server abort: ",r," (",r.name,")"),s(D),j&&clearTimeout(j),j=void 0}}var r=f.attr2("target"),i=f.attr2("action");w.setAttribute("target",d),(!u||/post/i.test(u))&&w.setAttribute("method","POST"),i!=m.url&&w.setAttribute("action",m.url),m.skipEncodingOverride||u&&!/post/i.test(u)||f.attr({encoding:"multipart/form-data",enctype:"multipart/form-data"}),m.timeout&&(j=setTimeout(function(){T=!0,s(k)},m.timeout));var o=[];try{if(m.extraData)for(var l in m.extraData)m.extraData.hasOwnProperty(l)&&(e.isPlainObject(m.extraData[l])&&m.extraData[l].hasOwnProperty("name")&&m.extraData[l].hasOwnProperty("value")?o.push(e('<input type="hidden" name="'+m.extraData[l].name+'">').val(m.extraData[l].value).appendTo(w)[0]):o.push(e('<input type="hidden" name="'+l+'">').val(m.extraData[l]).appendTo(w)[0]));m.iframeTarget||v.appendTo("body"),g.attachEvent?g.attachEvent("onload",s):g.addEventListener("load",s,!1),setTimeout(t,15);try{w.submit()}catch(c){var p=document.createElement("form").submit;p.apply(w)}}finally{w.setAttribute("action",i),r?w.setAttribute("target",r):f.removeAttr("target"),e(o).remove()}}function s(t){if(!x.aborted&&!F){if(M=n(g),M||(a("cannot access response document"),t=D),t===k&&x)return x.abort("timeout"),S.reject(x,"timeout"),void 0;if(t==D&&x)return x.abort("server abort"),S.reject(x,"error","server abort"),void 0;if(M&&M.location.href!=m.iframeSrc||T){g.detachEvent?g.detachEvent("onload",s):g.removeEventListener("load",s,!1);var r,i="success";try{if(T)throw"timeout";var o="xml"==m.dataType||M.XMLDocument||e.isXMLDoc(M);if(a("isXml="+o),!o&&window.opera&&(null===M.body||!M.body.innerHTML)&&--O)return a("requeing onLoad callback, DOM not available"),setTimeout(s,250),void 0;var u=M.body?M.body:M.documentElement;x.responseText=u?u.innerHTML:null,x.responseXML=M.XMLDocument?M.XMLDocument:M,o&&(m.dataType="xml"),x.getResponseHeader=function(e){var t={"content-type":m.dataType};return t[e.toLowerCase()]},u&&(x.status=Number(u.getAttribute("status"))||x.status,x.statusText=u.getAttribute("statusText")||x.statusText);var l=(m.dataType||"").toLowerCase(),c=/(json|script|text)/.test(l);if(c||m.textarea){var f=M.getElementsByTagName("textarea")[0];if(f)x.responseText=f.value,x.status=Number(f.getAttribute("status"))||x.status,x.statusText=f.getAttribute("statusText")||x.statusText;else if(c){var d=M.getElementsByTagName("pre")[0],h=M.getElementsByTagName("body")[0];d?x.responseText=d.textContent?d.textContent:d.innerText:h&&(x.responseText=h.textContent?h.textContent:h.innerText)}}else"xml"==l&&!x.responseXML&&x.responseText&&(x.responseXML=X(x.responseText));try{E=_(x,l,m)}catch(b){i="parsererror",x.error=r=b||i}}catch(b){a("error caught: ",b),i="error",x.error=r=b||i}x.aborted&&(a("upload aborted"),i=null),x.status&&(i=x.status>=200&&300>x.status||304===x.status?"success":"error"),"success"===i?(m.success&&m.success.call(m.context,E,"success",x),S.resolve(x.responseText,"success",x),p&&e.event.trigger("ajaxSuccess",[x,m])):i&&(void 0===r&&(r=x.statusText),m.error&&m.error.call(m.context,x,i,r),S.reject(x,"error",r),p&&e.event.trigger("ajaxError",[x,m,r])),p&&e.event.trigger("ajaxComplete",[x,m]),p&&!--e.active&&e.event.trigger("ajaxStop"),m.complete&&m.complete.call(m.context,x,i),F=!0,m.timeout&&clearTimeout(j),setTimeout(function(){m.iframeTarget?v.attr("src",m.iframeSrc):v.remove(),x.responseXML=null},100)}}}var l,c,m,p,d,v,g,x,b,y,T,j,w=f[0],S=e.Deferred();if(S.abort=function(e){x.abort(e)},r)for(c=0;h.length>c;c++)l=e(h[c]),i?l.prop("disabled",!1):l.removeAttr("disabled");if(m=e.extend(!0,{},e.ajaxSettings,t),m.context=m.context||m,d="jqFormIO"+(new Date).getTime(),m.iframeTarget?(v=e(m.iframeTarget),y=v.attr2("name"),y?d=y:v.attr2("name",d)):(v=e('<iframe name="'+d+'" src="'+m.iframeSrc+'" />'),v.css({position:"absolute",top:"-1000px",left:"-1000px"})),g=v[0],x={aborted:0,responseText:null,responseXML:null,status:0,statusText:"n/a",getAllResponseHeaders:function(){},getResponseHeader:function(){},setRequestHeader:function(){},abort:function(t){var r="timeout"===t?"timeout":"aborted";a("aborting upload... "+r),this.aborted=1;try{g.contentWindow.document.execCommand&&g.contentWindow.document.execCommand("Stop")}catch(n){}v.attr("src",m.iframeSrc),x.error=r,m.error&&m.error.call(m.context,x,r,t),p&&e.event.trigger("ajaxError",[x,m,r]),m.complete&&m.complete.call(m.context,x,r)}},p=m.global,p&&0===e.active++&&e.event.trigger("ajaxStart"),p&&e.event.trigger("ajaxSend",[x,m]),m.beforeSend&&m.beforeSend.call(m.context,x,m)===!1)return m.global&&e.active--,S.reject(),S;if(x.aborted)return S.reject(),S;b=w.clk,b&&(y=b.name,y&&!b.disabled&&(m.extraData=m.extraData||{},m.extraData[y]=b.value,"image"==b.type&&(m.extraData[y+".x"]=w.clk_x,m.extraData[y+".y"]=w.clk_y)));var k=1,D=2,A=e("meta[name=csrf-token]").attr("content"),L=e("meta[name=csrf-param]").attr("content");L&&A&&(m.extraData=m.extraData||{},m.extraData[L]=A),m.forceSync?o():setTimeout(o,10);var E,M,F,O=50,X=e.parseXML||function(e,t){return window.ActiveXObject?(t=new ActiveXObject("Microsoft.XMLDOM"),t.async="false",t.loadXML(e)):t=(new DOMParser).parseFromString(e,"text/xml"),t&&t.documentElement&&"parsererror"!=t.documentElement.nodeName?t:null},C=e.parseJSON||function(e){return window.eval("("+e+")")},_=function(t,r,a){var n=t.getResponseHeader("content-type")||"",i="xml"===r||!r&&n.indexOf("xml")>=0,o=i?t.responseXML:t.responseText;return i&&"parsererror"===o.documentElement.nodeName&&e.error&&e.error("parsererror"),a&&a.dataFilter&&(o=a.dataFilter(o,r)),"string"==typeof o&&("json"===r||!r&&n.indexOf("json")>=0?o=C(o):("script"===r||!r&&n.indexOf("javascript")>=0)&&e.globalEval(o)),o};return S}if(!this.length)return a("ajaxSubmit: skipping submit process - no element selected"),this;var u,l,c,f=this;"function"==typeof t?t={success:t}:void 0===t&&(t={}),u=t.type||this.attr2("method"),l=t.url||this.attr2("action"),c="string"==typeof l?e.trim(l):"",c=c||window.location.href||"",c&&(c=(c.match(/^([^#]+)/)||[])[1]),t=e.extend(!0,{url:c,success:e.ajaxSettings.success,type:u||e.ajaxSettings.type,iframeSrc:/^https/i.test(window.location.href||"")?"javascript:false":"about:blank"},t);var m={};if(this.trigger("form-pre-serialize",[this,t,m]),m.veto)return a("ajaxSubmit: submit vetoed via form-pre-serialize trigger"),this;if(t.beforeSerialize&&t.beforeSerialize(this,t)===!1)return a("ajaxSubmit: submit aborted via beforeSerialize callback"),this;var p=t.traditional;void 0===p&&(p=e.ajaxSettings.traditional);var d,h=[],v=this.formToArray(t.semantic,h);if(t.data&&(t.extraData=t.data,d=e.param(t.data,p)),t.beforeSubmit&&t.beforeSubmit(v,this,t)===!1)return a("ajaxSubmit: submit aborted via beforeSubmit callback"),this;if(this.trigger("form-submit-validate",[v,this,t,m]),m.veto)return a("ajaxSubmit: submit vetoed via form-submit-validate trigger"),this;var g=e.param(v,p);d&&(g=g?g+"&"+d:d),"GET"==t.type.toUpperCase()?(t.url+=(t.url.indexOf("?")>=0?"&":"?")+g,t.data=null):t.data=g;var x=[];if(t.resetForm&&x.push(function(){f.resetForm()}),t.clearForm&&x.push(function(){f.clearForm(t.includeHidden)}),!t.dataType&&t.target){var b=t.success||function(){};x.push(function(r){var a=t.replaceTarget?"replaceWith":"html";e(t.target)[a](r).each(b,arguments)})}else t.success&&x.push(t.success);if(t.success=function(e,r,a){for(var n=t.context||this,i=0,o=x.length;o>i;i++)x[i].apply(n,[e,r,a||f,f])},t.error){var y=t.error;t.error=function(e,r,a){var n=t.context||this;y.apply(n,[e,r,a,f])}}if(t.complete){var T=t.complete;t.complete=function(e,r){var a=t.context||this;T.apply(a,[e,r,f])}}var j=e("input[type=file]:enabled",this).filter(function(){return""!==e(this).val()}),w=j.length>0,S="multipart/form-data",k=f.attr("enctype")==S||f.attr("encoding")==S,D=n.fileapi&&n.formdata;a("fileAPI :"+D);var A,L=(w||k)&&!D;t.iframe!==!1&&(t.iframe||L)?t.closeKeepAlive?e.get(t.closeKeepAlive,function(){A=s(v)}):A=s(v):A=(w||k)&&D?o(v):e.ajax(t),f.removeData("jqxhr").data("jqxhr",A);for(var E=0;h.length>E;E++)h[E]=null;return this.trigger("form-submit-notify",[this,t]),this},e.fn.ajaxForm=function(n){if(n=n||{},n.delegation=n.delegation&&e.isFunction(e.fn.on),!n.delegation&&0===this.length){var i={s:this.selector,c:this.context};return!e.isReady&&i.s?(a("DOM not ready, queuing ajaxForm"),e(function(){e(i.s,i.c).ajaxForm(n)}),this):(a("terminating; zero elements found by selector"+(e.isReady?"":" (DOM not ready)")),this)}return n.delegation?(e(document).off("submit.form-plugin",this.selector,t).off("click.form-plugin",this.selector,r).on("submit.form-plugin",this.selector,n,t).on("click.form-plugin",this.selector,n,r),this):this.ajaxFormUnbind().bind("submit.form-plugin",n,t).bind("click.form-plugin",n,r)},e.fn.ajaxFormUnbind=function(){return this.unbind("submit.form-plugin click.form-plugin")},e.fn.formToArray=function(t,r){var a=[];if(0===this.length)return a;var i=this[0],o=t?i.getElementsByTagName("*"):i.elements;if(!o)return a;var s,u,l,c,f,m,p;for(s=0,m=o.length;m>s;s++)if(f=o[s],l=f.name,l&&!f.disabled)if(t&&i.clk&&"image"==f.type)i.clk==f&&(a.push({name:l,value:e(f).val(),type:f.type}),a.push({name:l+".x",value:i.clk_x},{name:l+".y",value:i.clk_y}));else if(c=e.fieldValue(f,!0),c&&c.constructor==Array)for(r&&r.push(f),u=0,p=c.length;p>u;u++)a.push({name:l,value:c[u]});else if(n.fileapi&&"file"==f.type){r&&r.push(f);var d=f.files;if(d.length)for(u=0;d.length>u;u++)a.push({name:l,value:d[u],type:f.type});else a.push({name:l,value:"",type:f.type})}else null!==c&&c!==void 0&&(r&&r.push(f),a.push({name:l,value:c,type:f.type,required:f.required}));if(!t&&i.clk){var h=e(i.clk),v=h[0];l=v.name,l&&!v.disabled&&"image"==v.type&&(a.push({name:l,value:h.val()}),a.push({name:l+".x",value:i.clk_x},{name:l+".y",value:i.clk_y}))}return a},e.fn.formSerialize=function(t){return e.param(this.formToArray(t))},e.fn.fieldSerialize=function(t){var r=[];return this.each(function(){var a=this.name;if(a){var n=e.fieldValue(this,t);if(n&&n.constructor==Array)for(var i=0,o=n.length;o>i;i++)r.push({name:a,value:n[i]});else null!==n&&n!==void 0&&r.push({name:this.name,value:n})}}),e.param(r)},e.fn.fieldValue=function(t){for(var r=[],a=0,n=this.length;n>a;a++){var i=this[a],o=e.fieldValue(i,t);null===o||void 0===o||o.constructor==Array&&!o.length||(o.constructor==Array?e.merge(r,o):r.push(o))}return r},e.fieldValue=function(t,r){var a=t.name,n=t.type,i=t.tagName.toLowerCase();if(void 0===r&&(r=!0),r&&(!a||t.disabled||"reset"==n||"button"==n||("checkbox"==n||"radio"==n)&&!t.checked||("submit"==n||"image"==n)&&t.form&&t.form.clk!=t||"select"==i&&-1==t.selectedIndex))return null;if("select"==i){var o=t.selectedIndex;if(0>o)return null;for(var s=[],u=t.options,l="select-one"==n,c=l?o+1:u.length,f=l?o:0;c>f;f++){var m=u[f];if(m.selected){var p=m.value;if(p||(p=m.attributes&&m.attributes.value&&!m.attributes.value.specified?m.text:m.value),l)return p;s.push(p)}}return s}return e(t).val()},e.fn.clearForm=function(t){return this.each(function(){e("input,select,textarea",this).clearFields(t)})},e.fn.clearFields=e.fn.clearInputs=function(t){var r=/^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i;return this.each(function(){var a=this.type,n=this.tagName.toLowerCase();r.test(a)||"textarea"==n?this.value="":"checkbox"==a||"radio"==a?this.checked=!1:"select"==n?this.selectedIndex=-1:"file"==a?/MSIE/.test(navigator.userAgent)?e(this).replaceWith(e(this).clone(!0)):e(this).val(""):t&&(t===!0&&/hidden/.test(a)||"string"==typeof t&&e(this).is(t))&&(this.value="")})},e.fn.resetForm=function(){return this.each(function(){("function"==typeof this.reset||"object"==typeof this.reset&&!this.reset.nodeType)&&this.reset()})},e.fn.enable=function(e){return void 0===e&&(e=!0),this.each(function(){this.disabled=!e})},e.fn.selected=function(t){return void 0===t&&(t=!0),this.each(function(){var r=this.type;if("checkbox"==r||"radio"==r)this.checked=t;else if("option"==this.tagName.toLowerCase()){var a=e(this).parent("select");t&&a[0]&&"select-one"==a[0].type&&a.find("option").selected(!1),this.selected=t}})},e.fn.ajaxSubmit.debug=!1})("undefined"!=typeof jQuery?jQuery:window.Zepto); \ No newline at end of file
--- a/static/js/markitup/sets/markdown/set.js Thu Sep 19 20:00:10 2013 -0500 +++ b/static/js/markitup/sets/markdown/set.js Sat Sep 21 17:00:49 2013 -0500 @@ -126,4 +126,55 @@ } return false; }); + + var $photoProgress = $('#photo-upload-progress'); + var $photoForm = $('#photo-upload-form'); + var $postBox = $('#id_body'); + var $photoUploadSubmit = $('#photo-upload-submit'); + + $photoForm.ajaxForm({ + beforeSubmit: function(arr, $form, options) { + var fileObj = null; + $.each(arr, function(index, val) { + if (val.name == 'image_file') { + fileObj = val.value; + } + }); + if (!fileObj) { + alert("Please choose a file to upload."); + return false; + } + $photoUploadSubmit.attr('disabled', 'disabled').val('Uploading...'); + return true; + }, + beforeSend: function() { + $photoProgress.progressbar({value: 0}); + }, + uploadProgress: function(event, position, total, percentComplete) { + if (percentComplete < 100) { + $photoProgress.progressbar({value: percentComplete}); + } + else { + $photoProgress.progressbar({value: false}); + } + }, + success: function(resp, statusText, xhr, $form) { + $photoProgress.progressbar({value: 100}); + if (resp.success) { + $postBox.val($postBox.val() + '\n![image](' + resp.url + ')'); + alert("Success! The image code was added to your post."); + } + else { + alert('Error: ' + resp.msg); + } + }, + complete: function(xhr) { + $photoProgress.progressbar({value: 0}); + $photoForm.clearForm(); + $photoUploadSubmit.removeAttr('disabled').val('Upload photo'); + }, + error: function(xhr, textStatus, ex) { + alert('Oops, there was an error: ' + ex); + } + }); });
--- a/user_photos/urls.py Thu Sep 19 20:00:10 2013 -0500 +++ b/user_photos/urls.py Sat Sep 21 17:00:49 2013 -0500 @@ -8,6 +8,9 @@ urlpatterns = patterns('', url(r'^upload/$', 'user_photos.views.upload', name='user_photos-upload'), + url(r'^upload-ajax/$', + 'user_photos.views.upload_ajax', + name='user_photos-upload_ajax'), url(r'^photo/(?P<pk>\d+)/$', DetailView.as_view(model=Photo), name='user_photos-detail'),
--- a/user_photos/views.py Thu Sep 19 20:00:10 2013 -0500 +++ b/user_photos/views.py Sat Sep 21 17:00:49 2013 -0500 @@ -1,7 +1,11 @@ """Views for the user_photos application.""" +import json + from django.conf import settings from django.contrib.auth import get_user_model from django.contrib.auth.decorators import login_required +from django.http import (HttpResponse, HttpResponseForbidden, + HttpResponseNotAllowed) from django.shortcuts import render, redirect, get_object_or_404 from django.views.generic import ListView from django.views.decorators.http import require_POST @@ -20,10 +24,6 @@ created. The image and thumbnail will then be uploaded to the Amazon S3 service for storage. - TODO: rate limiting - pass off the processing to a celery task - ajax version of this view - """ form = None uploads_enabled = settings.USER_PHOTOS_ENABLED @@ -44,6 +44,46 @@ status=200 if uploads_enabled else 503) +def upload_ajax(request): + """This view is the ajax version of the upload function, above. + + We return a JSON object response to the client with the following name + & value pairs: + 'success' : false for failure and true for success + 'msg' : string error message if success is false + 'url' : the image URL as a string if success + + If a non-200 status code is returned the response will simply be a text + string error message. + + """ + if not request.user.is_authenticated(): + return HttpResponseForbidden + if not request.is_ajax() or request.method != 'POST': + return HttpResponseNotAllowed + + ret = {'success': False, 'msg': '', 'url': ''} + if settings.USER_PHOTOS_ENABLED: + form = UploadForm(request.POST, request.FILES, user=request.user) + if form.is_valid(): + photo = form.save() + ret['success'] = True + ret['url'] = photo.url + else: + # gather form error messages + errors = [] + non_field_errors = form.non_field_errors().as_text() + if non_field_errors: + errors.append(non_field_errors) + for field_errors in form.errors.values(): + errors.append(field_errors.as_text()) + ret['msg'] = '\n'.join(errors) + else: + ret['msg'] = 'Photo uploads are temporarily disabled' + + return HttpResponse(json.dumps(ret), content_type='application/json') + + class GalleryView(ListView): """A ListView for displaying a user's photos"""