Code

Ticket #4165: 6602_javascript_and_admin.diff

File 6602_javascript_and_admin.diff, 16.0 KB (added by Faheem Mitha <faheem@…>, 7 years ago)

Update progress bar patch to 6602, with corrections. This should work with 6525_all_tests_pass.diff in issue 2070.

Line 
1diff -r 8c85b71cb6b6 django/contrib/admin/media/js/UploadProgress.js
2--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
3+++ b/django/contrib/admin/media/js/UploadProgress.js   Thu Oct 25 17:15:14 2007 -0400
4@@ -0,0 +1,205 @@
5+
6+var upload_progress_failures = 0;
7+var TOTAL_UPLOAD_PROGRESS_FAILURES = 10;
8+var upload_progress_num__IE = 0;
9+var show_progress = false;
10+/* below is the url from admin to the upload progress.
11+   e.g. upload_progress/
12+*/
13+var progress_url  = "upload_progress/";
14+
15+var split_path = location.pathname.split('/');
16+
17+var new_array = new Array();
18+
19+var j=0;
20+for (var i=0; i<split_path.length; i++) {
21+    if (split_path[i] != '') {
22+        new_array[j] = split_path[i];
23+        j++;
24+    }
25+}
26+
27+admin_root = '/';
28+if (new_array.length > 3) {
29+    for (var i=0; i<new_array.length-3; i++) {
30+        admin_root += new_array[i]+'/';
31+    }
32+}
33+
34+progress_url = admin_root + progress_url;
35+
36+function getxy(){
37+    var x,y;
38+    if (self.innerHeight) // all except Explorer
39+        {
40+        x = self.innerWidth;
41+        y = self.innerHeight;
42+        }
43+    else if (document.documentElement && document.documentElement.clientHeight)
44+        // Explorer 6 Strict Mode
45+        {
46+        x = document.documentElement.clientWidth;
47+        y = document.documentElement.clientHeight;
48+        }
49+    else if (document.body) // other Explorers
50+        {
51+        x = document.body.clientWidth;
52+        y = document.body.clientHeight;
53+        }
54+    return {'x':x,'y':y}
55+    }
56+
57+
58+function upload_ajax_problem() {
59+    /* If there's a problem, will cancel after
60+       TOTAL_UPLOAD_PROGRESS_FAILURES tries.   */
61+
62+    var progress_wrap2 = document.getElementById('progress_wrap');
63+    upload_progress_failures++;
64+    if (upload_progress_failures >= TOTAL_UPLOAD_PROGRESS_FAILURES){
65+      window.clearTimeout(interval);
66+    }
67+    progress_wrap2.style.display = 'none';
68+    progress_wrap2.style.visibility = 'hidden';
69+}
70+
71+var humanvalue = ['B','KB','MB','GB']
72+function humanize(bytes) {
73+  curbytes = bytes;
74+  iterations = 0;
75+  if (!curbytes) {
76+     return '';
77+  }
78+  while (curbytes>1024) {
79+    iterations++;
80+    curbytes=curbytes/1024;
81+  }
82+  return curbytes.toFixed(1) + ' ' + humanvalue[iterations];
83+}
84+
85+interval = null;
86+function fetch(uuid) {
87+  /* no ajax here */
88+  if (!xmlhttp) {
89+    upload_ajax_problem();
90+    return;
91+  }
92+
93+  req = xmlhttp;
94+  req.open("GET", progress_url+"?" + upload_progress_num__IE, true);
95+  upload_progress_num__IE++; // IE Hack
96+  req.onreadystatechange = function () {
97+    var progress_wrap2 = document.getElementById('progress_wrap');
98+    var progress_bar2  = document.getElementById('progress_bar');
99+    var bar_txt = document.getElementById('progress_text');
100+
101+    if (req.readyState == 4) {
102+      try {
103+        request_status = req.status;
104+      } catch (e) {
105+        /* Really bad. */
106+        request_status = -1;
107+      }
108+      if (request_status == 200) {
109+        var upload = new Function(" return "+req.responseText)();
110+        if (upload) {
111+
112+          if (!upload.state) {
113+             progress_wrap2.style.visibility = 'hidden';
114+             progress_wrap2.style.display    = 'none';
115+             return;
116+          } else if (upload.state == 'done') {
117+             window.clearTimeout(interval);
118+             progress_wrap2.style.visibility = 'hidden';
119+             progress_wrap2.style.display    = 'none';
120+             return;   
121+          } else {
122+             if (show_progress) {
123+                 progress_wrap2.style.visibility = 'visible';
124+                 progress_wrap2.style.display    = 'block';
125+             }
126+             move_to_center(progress_wrap2);
127+             bar_txt.innerHTML = ((upload.received / upload.size) * 100).toFixed(1)
128+                     + '% - ' + humanize(upload.received) + ' of '
129+                     + humanize(upload.size);
130+             var bar_width__px = 400 * upload.received / upload.size;
131+             progress_bar2.style.width = bar_width__px + 'px';
132+          }
133+        } else {
134+          upload_ajax_problem();
135+        }
136+      } else {
137+        upload_ajax_problem();
138+      }
139+    }
140+  };
141+  try {
142+    req.setRequestHeader("X-Upload-Id", uuid);
143+  } catch (e) {
144+    /* couldn't set the header, the request is broken. */
145+    req.abort();
146+  }
147+  req.send(null);
148+
149+}
150+
151+function move_to_center(progress_wrap) {
152+    pos = getxy();
153+    posx = parseInt((pos.x/2)-(420/2), 10);
154+    posy = parseInt((pos.y/2)-(50/2), 10);
155+
156+    progress_wrap.style.top  = posy + 'px';
157+    progress_wrap.style.left = posx + 'px';   
158+}
159+
160+function close_progress() {
161+    var progress_wrap2 = document.getElementById('progress_wrap');
162+    progress_wrap2.style.display = 'none';
163+    progress_wrap2.style.visibility = 'hidden';
164+    show_progress = false;
165+    // don't want to follow a link.
166+    return false;
167+}
168+
169+
170+function openprogress(e) {
171+
172+    show_progress = true;
173+    upload_progress_failures = 0;
174+    uuid = "";
175+    for (i = 0; i < 32; i++) {
176+        uuid += Math.floor(Math.random() * 16).toString(16);
177+        }
178+    frm = e.target||e.srcElement;
179+
180+    /*
181+    var progress_wrap2 = document.getElementById('progress_wrap');
182+    move_to_center(progress_wrap2);
183+    progress_wrap2.style.display = 'block';
184+    progress_wrap2.style.visibility = 'visible';
185+    */
186+
187+    if (frm.action.indexOf('?') == -1) {
188+       frm.action=frm.action+"?progress_id=" + uuid
189+    } else {
190+       frm.action=frm.action+"&progress_id=" + uuid;
191+    }
192+
193+    interval = window.setInterval(
194+        function () {
195+            fetch(uuid);
196+            },
197+        1000
198+        );
199+}
200+
201+addEvent(window, 'load', function() {
202+        frms = document.getElementsByTagName('form');
203+        for (var i=0; i<frms.length; i++) {
204+           if (frms[i].encoding.toLowerCase() == 'multipart/form-data') {
205+              addEvent(frms[i], 'submit',  openprogress);
206+              return;
207+           }
208+        }
209+    });
210diff -r 8c85b71cb6b6 django/contrib/admin/templates/admin/change_form.html
211--- a/django/contrib/admin/templates/admin/change_form.html     Thu Oct 25 14:12:04 2007 -0400
212+++ b/django/contrib/admin/templates/admin/change_form.html     Thu Oct 25 17:15:14 2007 -0400
213@@ -64,6 +64,18 @@
214    {% auto_populated_field_script auto_populated_fields change %}
215    </script>
216 {% endif %}
217+
218+{% if has_file_field %}
219+<div id="progress_wrap" style="position: absolute; background: white; z-index: 9040; display: none; visibility: hidden; width: 420px; height: 50px padding: 10px; border: solid 1px #ddd;">
220+   <a href="#" onclick="close_progress();return false" title="Close Progress Bar"
221+      style="color: #c00; font-size: 1.5em; font-weight: bold; float: right; padding: 0; position: relative; top: -2px; left: -2px;">X</a>
222+   <h1>Upload progress</h1>
223+
224+   <div id="progress_bar" style="top: 0; left: 0; width: 0; z-index: 9049; height: 4px;" class="submit-row"></div>
225+   <div id="progress_text" style="color: black;">0%</div>
226+</div>
227+{% endif %}
228+
229 </div>
230 </form></div>
231 {% endblock %}
232diff -r 8c85b71cb6b6 django/contrib/admin/urls.py
233--- a/django/contrib/admin/urls.py      Thu Oct 25 14:12:04 2007 -0400
234+++ b/django/contrib/admin/urls.py      Thu Oct 25 17:15:14 2007 -0400
235@@ -10,6 +10,7 @@ urlpatterns = patterns('',
236     ('^$', 'django.contrib.admin.views.main.index'),
237     ('^r/(\d+)/(.*)/$', 'django.views.defaults.shortcut'),
238     ('^jsi18n/$', i18n_view, {'packages': 'django.conf'}),
239+    ('^upload_progress/$', 'django.contrib.admin.views.main.upload_progress'),
240     ('^logout/$', 'django.contrib.auth.views.logout'),
241     ('^password_change/$', 'django.contrib.auth.views.password_change'),
242     ('^password_change/done/$', 'django.contrib.auth.views.password_change_done'),
243diff -r 8c85b71cb6b6 django/contrib/admin/views/main.py
244--- a/django/contrib/admin/views/main.py        Thu Oct 25 14:12:04 2007 -0400
245+++ b/django/contrib/admin/views/main.py        Thu Oct 25 17:15:14 2007 -0400
246@@ -9,7 +9,7 @@ from django.shortcuts import get_object_
247 from django.shortcuts import get_object_or_404, render_to_response
248 from django.db import models
249 from django.db.models.query import handle_legacy_orderlist, QuerySet
250-from django.http import Http404, HttpResponse, HttpResponseRedirect
251+from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpResponseServerError
252 from django.utils.html import escape
253 from django.utils.text import capfirst, get_text_list
254 from django.utils.encoding import force_unicode, smart_str
255@@ -88,6 +88,8 @@ def get_javascript_imports(opts, auto_po
256 def get_javascript_imports(opts, auto_populated_fields, field_sets):
257 # Put in any necessary JavaScript imports.
258     js = ['js/core.js', 'js/admin/RelatedObjectLookups.js']
259+    if opts.has_field_type(models.FileField) and settings.FILE_UPLOAD_DIR:
260+        js.append('js/UploadProgress.js')
261     if auto_populated_fields:
262         js.append('js/urlify.js')
263     if opts.has_field_type(models.DateTimeField) or opts.has_field_type(models.TimeField) or opts.has_field_type(models.DateField):
264@@ -789,3 +791,19 @@ def change_list(request, app_label, mode
265                                'admin/%s/change_list.html' % app_label,
266                                'admin/change_list.html'], context_instance=c)
267 change_list = staff_member_required(never_cache(change_list))
268+
269+def upload_progress(request):
270+    """
271+    Given this request, returns a JSON
272+    object that has information on a file upload progress.
273+    If there is no file upload in progress, returns an
274+    empty dictionary, '{}'.
275+    """
276+    from django.utils import simplejson
277+
278+    content = simplejson.dumps(request.file_progress)
279+
280+    if content.strip() == '{}':
281+        return HttpResponseServerError('')
282+    else:
283+        return HttpResponse(content=content, mimetype='text/plain')
284diff -r 8c85b71cb6b6 django/contrib/uploadprogress/__init__.py
285--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
286+++ b/django/contrib/uploadprogress/__init__.py Thu Oct 25 17:15:14 2007 -0400
287@@ -0,0 +1,1 @@
288+
289diff -r 8c85b71cb6b6 django/contrib/uploadprogress/middleware/__init__.py
290--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
291+++ b/django/contrib/uploadprogress/middleware/__init__.py      Thu Oct 25 17:15:14 2007 -0400
292@@ -0,0 +1,3 @@
293+from django.contrib.uploadprogress.middleware.uploaddb import FileProgressDB
294+from django.contrib.uploadprogress.middleware.uploadcache import FileProgressCached
295+
296diff -r 8c85b71cb6b6 django/contrib/uploadprogress/middleware/uploadcache.py
297--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
298+++ b/django/contrib/uploadprogress/middleware/uploadcache.py   Thu Oct 25 17:15:14 2007 -0400
299@@ -0,0 +1,59 @@
300+"""
301+
302+Middleware to track file progress using the cache framework.
303+To use, just add
304+    'django.contrib.uploadprogress.middleware.FileProgressCached'
305+to your ``MIDDLEWARE_SETTINGS``.
306+
307+If your cache framework does not work, this will not work either.
308+
309+"""
310+from django.core.cache import cache
311+from django.conf import settings
312+from django.http.multipartparser import MultiPartParserError
313+
314+UPLOAD_CACHE_PREFIX = getattr(settings, 'UPLOAD_CACHE_PREFIX', 'UPLOAD_PROGRESS_')
315+
316+class FileProgressStore(object):
317+
318+    def _get_key(self, request):
319+        """
320+        Returns the cache prefix for any cache key.
321+        Uses the IP Address as well as the randomly generated uuid.
322+        """
323+       
324+        if hasattr(self, '_cache_key'):
325+            return self._cache_key
326+       
327+        self._cache_key = '%s__%s__%s' % \
328+                           (UPLOAD_CACHE_PREFIX,
329+                            request.META['REMOTE_ADDR'],
330+                            request.META['UPLOAD_PROGRESS_ID'],)
331+
332+        return self._cache_key
333+
334+    def __get__(self, request, HttpRequest):
335+        return cache.get(self._get_key(request), {})
336+
337+    def __set__(self, request, new_val):
338+        received_size = total_size = -1
339+        try:
340+            total_size = int(new_val['size'])
341+        except:
342+            pass
343+
344+        try:
345+            received_size = int(new_val['received'])
346+        except:
347+            pass
348+       
349+        cache.set(self._get_key(request), new_val)
350+
351+    def __delete__(self, request):
352+        cache.delete(self._get_key(request))
353+
354+class FileProgressCached(object):
355+
356+    def process_request(self, request):
357+        # set the request.file_progress descriptor
358+        request.__class__.file_progress = FileProgressStore()
359diff -r 8c85b71cb6b6 django/contrib/uploadprogress/middleware/uploaddb.py
360--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
361+++ b/django/contrib/uploadprogress/middleware/uploaddb.py      Thu Oct 25 17:15:14 2007 -0400
362@@ -0,0 +1,47 @@
363+"""
364+
365+Middleware to hold fileupload progress state in a database.
366+To install, simply add
367+    'django.contrib.uploadprogress.middleware.FileProgressDB'
368+   
369+to your ``MIDDLEWARE_SETTINGS`` and
370+    'django.contrib.uploadprogress'
371+to your ``INSTALLED_APPS``.
372+
373+Then run ``./manage.py syncdb`` as you normally would.
374+
375+"""
376+from django.contrib.uploadprogress.models import FileProgress
377+from django.http.multipartparser import MultiPartParserError
378+from django.conf import settings
379+
380+try:
381+    import cPickle as pickle
382+except ImportError:
383+    import pickle
384+
385+class FileProgressDBStore(object):
386+
387+    def _get_db_row(self, request):
388+        if not hasattr(self, '_db_row'):
389+            self._db_row, created = FileProgress.objects.get_or_create(
390+                                                 remote_addr = request.META['REMOTE_ADDR'],
391+                                                 progress_id = request.META['UPLOAD_PROGRESS_ID'])
392+
393+        return self._db_row
394+
395+    def __get__(self, request, HttpRequest):
396+        return self._get_db_row(request).progress
397+
398+    def __set__(self, request, new_val):
399+        row = self._get_db_row(request)
400+        row.progress = new_val
401+        row.save()
402+
403+    def __delete__(self, request):
404+        self._get_db_row(request).delete()
405+
406+class FileProgressDB(object):
407+
408+    def process_request(self, request):
409+        request.__class__.file_progress = FileProgressDBStore()
410diff -r 8c85b71cb6b6 django/contrib/uploadprogress/models.py
411--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
412+++ b/django/contrib/uploadprogress/models.py   Thu Oct 25 17:15:14 2007 -0400
413@@ -0,0 +1,77 @@
414+"""
415+Models file for a simple file upload progress application.
416+This cause the file progress to be stored in the database.
417+To activate:
418+1) Add 'django.contrib.uploadprogress.middleware.FileProgressDB'
419+   to your MIDDLEWARE_CLASSES setting.
420+2) Add 'django.contrib.uploadprogress' to your INSTALLED_APPS.
421+
422+"""
423+
424+from django.db import models
425+import datetime
426+
427+try:
428+    import cPickle as pickle
429+except ImportError:
430+    import pickle
431+
432+class FileProgress(models.Model):
433+
434+    remote_addr = models.CharField(maxlength=64)
435+    progress_id = models.CharField(maxlength=32)
436+    progress_text = models.TextField(editable = False)
437+    last_ts     = models.DateTimeField()
438+
439+    class Meta:
440+        verbose_name_plural = 'File Progresses'
441+        unique_together = (('remote_addr',
442+                           'progress_id',
443+                           ),
444+                           )
445+
446+    class Admin:
447+        pass
448+
449+    def __str__(self):
450+        return 'File Progress for "%s" and IP "%s".' % (self.progress_id, self.remote_addr)
451+
452+    def _get_dict(self):
453+        """
454+        Returns a dictionary object.
455+        """
456+        if hasattr(self, '_progress_dict'):
457+            return self._progress_dict
458+       
459+        if not self.progress_text:
460+            self._progress_dict = {}
461+            return {}
462+
463+        try:
464+            self._progress_dict = pickle.loads(self.progress_text)
465+        except:
466+            self._progress_dict = {}
467+
468+        return self._progress_dict
469+
470+    def _set_dict(self, dict):
471+        """
472+        Sets a dictionary object.
473+        """
474+        self._progress_dict = dict
475+
476+    progress = property(_get_dict, _set_dict)
477+
478+    def save(self, *args, **kwargs):
479+        """
480+        Pickles the dictionary representation
481+        of the progress.
482+        """
483+        try:
484+            self.progress_text = pickle.dumps(self.progress)
485+        except:
486+            pass
487+
488+        self.last_ts = datetime.datetime.now()
489+
490+        return super(FileProgress, self).save(*args, **kwargs)