Mercurial > hg > agora-dellsystem
changeset 177:86129d185ddb
Add versioning to bundles
Some other bundle-related changes were made, including:
* Editing the snippetform CSS and HTML to allow bundle/form.djhtml to
be reused for editing
* Changing {% block title %} to {% block section %} in the base
template for bundles to allow for more flexibility when creating
breadcrumbs
* Saved common URL patterns in variables in bundle/urls.py
* Renamed explore.html to explore.djhtml for consistency
You should now be able to upload new versions as well as view the files
(or a particular file) for a bundle at a specific version.
Coming soon: the ability to add a timestamp and a comment for each new
uploaded version (if this feature is desirable).
author | dellsystem <ilostwaldo@gmail.com> |
---|---|
date | Sat, 20 Oct 2012 23:28:50 -0400 |
parents | c042d26e6936 |
children | df160069b769 |
files | apps/bundle/forms.py apps/bundle/models.py apps/bundle/tasks.py apps/bundle/urls.py apps/bundle/views.py static/css/agora.less templates/bundle/base.djhtml templates/bundle/bundle.djhtml templates/bundle/edit.djhtml templates/bundle/explore.djhtml templates/bundle/explore.html templates/bundle/form.djhtml templates/bundle/index.djhtml templates/simple_field.djhtml |
diffstat | 14 files changed, 215 insertions(+), 86 deletions(-) [+] |
line wrap: on
line diff
--- a/apps/bundle/forms.py +++ b/apps/bundle/forms.py @@ -6,7 +6,21 @@ class BundleForm(forms.ModelForm): class Meta: model = Bundle - fields = ('uploader', 'name', 'description', 'free_license') + fields = ('name', 'description', 'free_license') file = forms.FileField(help_text=("Upload a plain text file or an \ archive file.")) + + +class BundleEditForm(forms.ModelForm): + """ + Like BundleForm, but for editing bundles. A new form is needed because + the name field should not be editable after creation, and because the + file field shouldn't be required in this case + """ + class Meta: + model = Bundle + fields = ('description', 'free_license') + + file = forms.FileField(help_text=("Upload a plain text file or an \ + archive file to update the version."), required=False)
--- a/apps/bundle/models.py +++ b/apps/bundle/models.py @@ -28,6 +28,7 @@ mod_date = models.DateTimeField('date last modified', auto_now=True) done_uploading = models.BooleanField(default=False) file_name = models.CharField(max_length=256) # the uploaded file + latest_version = models.IntegerField(default=1) def __unicode__(self): return self.name @@ -37,11 +38,16 @@ return ('bundle_details', [self.uploader.username, self.name]) def get_temp_path(self): - return os.path.join('tmp', 'bundles', '%s' % self.id) + """ + Returns the path to where the file is initially uploaded + """ + return os.path.join('tmp', 'bundles', + "%d_%d" % (self.id, self.latest_version)) class BundleFile(MPTTModel): bundle = models.ForeignKey(Bundle) + version = models.IntegerField() parent = TreeForeignKey('self', null=True, blank=True, related_name='children') name = models.CharField(max_length=256) @@ -86,5 +92,6 @@ return ('bundlefile_details', [ self.bundle.uploader.username, self.bundle.name, + self.version, self.get_path() ])
--- a/apps/bundle/tasks.py +++ b/apps/bundle/tasks.py @@ -30,7 +30,8 @@ filename = os.path.basename(file_path) full_path = file_path[len(bundle.get_temp_path()) + 1:] bundle_file = BundleFile(bundle=bundle, name=filename, - parent=parent_dir, full_path=full_path) + parent=parent_dir, full_path=full_path, + version=bundle.latest_version) if file_path in files: bundle_file.is_dir = False @@ -79,7 +80,8 @@ elif mime_type.startswith('text/'): # Should be a plain text file - create a CodeFile for it bundle_file = BundleFile(bundle=bundle, name=bundle.file_name, - full_path=bundle.file_name, file_size=os.path.getsize(file)) + full_path=bundle.file_name, file_size=os.path.getsize(file), + version=bundle.latest_version) bundle_file.save_file_contents(open(file, 'rt'), original_filename=bundle.file_name)
--- a/apps/bundle/urls.py +++ b/apps/bundle/urls.py @@ -1,10 +1,17 @@ from django.conf.urls.defaults import * +BUNDLE_PATTERN = r'^(?P<user>[^/]*)/(?P<bundle>[^/]+)' +VERSION_PATTERN = '(?P<version>\d+)' + + urlpatterns = patterns('apps.bundle.views', - url(r'^(?P<user>[^/]+)/(?P<bundle>[^/]+)/(?P<path>.+)/$', 'file_detail', - name='bundlefile_details'), - url(r'^(?P<user>.*)/(?P<bundle>.*)/$', 'detail', name='bundle_details'), + url(BUNDLE_PATTERN + '/?$', 'detail', name='bundle_details'), + url(BUNDLE_PATTERN + '/' + VERSION_PATTERN + '/?$', 'detail', + name='bundle_version'), + url(BUNDLE_PATTERN + '/edit', 'edit', name='bundle_edit'), + url(BUNDLE_PATTERN + '/' + VERSION_PATTERN + '/(?P<path>.+)/?$', + 'file_detail', name='bundlefile_details'), url(r'^$', 'index', name='bundle_new'), url(r'^explore$', 'explore', name='bundle_explore'), )
--- a/apps/bundle/views.py +++ b/apps/bundle/views.py @@ -7,14 +7,16 @@ from django.http import HttpResponse from apps.bundle.models import Bundle, BundleFile -from apps.bundle.forms import BundleForm +from apps.bundle.forms import BundleForm, BundleEditForm from apps.bundle.tasks import handle_bundle_upload from apps.pygments_style.models import PygmentsStyle -def detail(request, user, bundle, file=None): +def detail(request, user, bundle, file=None, version=0): bundle = get_object_or_404(Bundle, uploader__username=user, name=bundle) - files = bundle.bundlefile_set.all() + # If the version is not set, use the latest version + version = int(version) or bundle.latest_version + files = bundle.bundlefile_set.filter(version=version) if request.user.is_authenticated(): pygments_style = request.user.get_profile().pygments_style @@ -27,28 +29,31 @@ 'bundle': bundle, 'files': files, 'file': file, + 'previous_versions': xrange(1, bundle.latest_version + 1), + 'this_version': version, } return render(request, 'bundle/bundle.djhtml', context) -def file_detail(request, user, bundle, path): +def file_detail(request, user, bundle, version, path): + print version bundle_file = get_object_or_404(BundleFile, bundle__uploader__username=user, - bundle__name=bundle, full_path=path, is_dir=False) + bundle__name=bundle, full_path=path, is_dir=False, version=version) - return detail(request, user, bundle, file=bundle_file) + return detail(request, user, bundle, file=bundle_file, version=version) + @login_required def index(request): if request.method == 'POST': post_data = request.POST.copy() - post_data['uploader'] = request.user.id - form = BundleForm(post_data, - request.FILES) + bundle = Bundle(uploader=request.user) + form = BundleForm(post_data, request.FILES, instance=bundle) if form.is_valid(): file = request.FILES.get('file') - bundle = form.save() + form.save() bundle.file_name = file.name bundle.save() @@ -76,4 +81,45 @@ 'recent_bundles': Bundle.objects.all()[:20] } - return render(request, "bundle/explore.html", context) + return render(request, "bundle/explore.djhtml", context) + + +@login_required +def edit(request, user, bundle): + bundle = get_object_or_404(Bundle, name=bundle, + uploader__username=request.user.username) + + # If the username specified in the URL is someone else's, show that page + if user != request.user.username: + # The bundle must exist, otherwise it would 404 + return redirect(bundle) + + if request.method == 'POST': + form = BundleEditForm(request.POST, instance=bundle) + + if form.is_valid(): + form.save() + + file = request.FILES.get('file') + if file is not None: + bundle.done_uploading = False + bundle.file_name = file.name + bundle.latest_version += 1 + bundle.save() + bundle_path = bundle.get_temp_path() + + with open(bundle_path, 'wb+') as destination: + for chunk in request.FILES.get('file', []): + destination.write(chunk) + + handle_bundle_upload.delay(bundle.id) + return redirect(bundle) + else: + form = BundleEditForm(instance=bundle) + + context = { + 'bundle': bundle, + 'form': form, + } + + return render(request, "bundle/edit.djhtml", context)
--- a/static/css/agora.less +++ b/static/css/agora.less @@ -190,6 +190,7 @@ .snippetform { input[type=text], textarea { + display: block; width: @nonSidebarWidth - @inputPadding * 2 - 2; padding: 5px; margin: 10px 0; @@ -415,3 +416,7 @@ margin-left: 0; padding-left: 18px; } + +.form-field { + margin-bottom: 10px; +}
--- a/templates/bundle/base.djhtml +++ b/templates/bundle/base.djhtml @@ -1,5 +1,5 @@ {% extends "base.djhtml" %} {% block breadcrumbs %} -<a href="{% url bundle_new %}">Bundles</a> » {% block title %}{% endblock %} +<a href="{% url bundle_new %}">Bundles</a> » {% block section %}{% endblock %} {% endblock %}
--- a/templates/bundle/bundle.djhtml +++ b/templates/bundle/bundle.djhtml @@ -5,16 +5,33 @@ {% load sizefieldtags %} +{% block section %} +<a href="{{ bundle.get_absolute_url }}">{{ bundle }}</a> +by <a href="{{ bundle.uploader.get_absolute_url }}">{{ bundle.uploader }}</a> +(version {{ this_version }}) +{% if file %} +» +{{ file }} +{% endif %} +{% endblock %} + {% block title %} -{{ bundle.name }} by {{ bundle.uploader }} -{% endblock title %} +{% if file %} +{{ file }} in +{% endif %} +{{ bundle }} by {{ bundle.uploader }} +{% endblock %} {% block content %} +<div class="right-float"> +<h2><a href="{% url bundle_edit request.user.username bundle.name %}">Edit this bundle</a></h2> +</div> <h1> <a href="{{ bundle.get_absolute_url }}">{{ bundle.name }}</a> by <a href="{{ bundle.uploader.get_absolute_url }}">{{ bundle.uploader }}</a> +(version {{ this_version }}) </h1> {% if not bundle.done_uploading %} @@ -57,6 +74,21 @@ {% else %} <p><strong>Description:</strong> {{ bundle.description|default:"N/A" }}</p> <p><strong>License:</strong> {{ bundle.free_license }}</p> + <p><strong>Latest version number:</strong> {{ bundle.latest_version }}</p> + {% if previous_versions %} + <h3>Versions</h3> + <ul> + {% for version in previous_versions %} + <li> + <a href="{{ bundle.get_absolute_url }}/{{ version }}"> + {% if version == this_version %}<strong>{% endif %} + Version {{ version }} + {% if version == this_version %}</strong>{% endif %} + </a> + </li> + {% endfor %} + </ul> + {% endif %} {% endif %} </div> {% else %}
new file mode 100644 --- /dev/null +++ b/templates/bundle/edit.djhtml @@ -0,0 +1,23 @@ +{% extends "bundle/base.djhtml" %} + + +{% block title %} +Editing {{ bundle }} +{% endblock %} + +{% block section %} +<a href="{{ bundle.get_absolute_url }}">{{ bundle }}</a> +» +Edit +{% endblock %} + +{% block content %} +<h1>Editing {{ bundle }}</h1> + +<p class="hint"> +You can't change a bundle's name after creating it. If you need to change +the name, simply create a new bundle using the desired name. +</p> + +{% include "bundle/form.djhtml" %} +{% endblock %}
new file mode 100644 --- /dev/null +++ b/templates/bundle/explore.djhtml @@ -0,0 +1,41 @@ +{% extends "bundle/base.djhtml" %} + + +{% load i18n %} + + +{% block title %} +{% trans "Explore" %} +{% endblock %} + + +{% block content %} +<h1>{% trans "Recent bundles" %}</h1> + +{% if recent_bundles %} +<table class="default"> + <thead> + <tr> + <th>{% trans "Bundle name" %}</th> + <th>{% trans "Created on" %}</th> + <th>{% trans "User" %}</th> + </tr> + </thead> + <tbody> + {% for bundle in recent_bundles %} + <tr> + <td><a href="{{ bundle.get_absolute_url }}">{{ bundle }}</a></td> + <td>{{ bundle.pub_date }}</td> + <td> + <a href="{{ bundle.uploader.get_absolute_url }}"> + {{ bundle.uploader }} + </a> + </td> + </tr> + {% endfor %} + </tbody> +</table> +{% else %} +<p>{% trans "No bundles have been created yet!" %}</p> +{% endif %} +{% endblock %}
deleted file mode 100644 --- a/templates/bundle/explore.html +++ /dev/null @@ -1,41 +0,0 @@ -{% extends "bundle/base.djhtml" %} - - -{% load i18n %} - - -{% block title %} -{% trans "Explore" %} -{% endblock %} - - -{% block content %} -<h1>{% trans "Recent bundles" %}</h1> - -{% if recent_bundles %} -<table class="default"> - <thead> - <tr> - <th>{% trans "Bundle name" %}</th> - <th>{% trans "Created on" %}</th> - <th>{% trans "User" %}</th> - </tr> - </thead> - <tbody> - {% for bundle in recent_bundles %} - <tr> - <td><a href="{{ bundle.get_absolute_url }}">{{ bundle }}</a></td> - <td>{{ bundle.pub_date }}</td> - <td> - <a href="{{ bundle.uploader.get_absolute_url }}"> - {{ bundle.uploader }} - </a> - </td> - </tr> - {% endfor %} - </tbody> -</table> -{% else %} -<p>{% trans "No bundles have been created yet!" %}</p> -{% endif %} -{% endblock %}
--- a/templates/bundle/form.djhtml +++ b/templates/bundle/form.djhtml @@ -10,23 +10,9 @@ {% csrf_token %} - {% with field=form.name %} - {% include "simple_field.djhtml" %} - {% endwith %} - - {% with field=form.description %} + {% for field in form %} {% include "simple_field.djhtml" %} - {% endwith %} - - {% with field=form.free_license %} - {% include "simple_field.djhtml" %} - {% endwith %} - - <br /><br /> - - {% with field=form.file %} - {% include "simple_field.djhtml" %} - {% endwith %} + {% endfor %} <br /> <div class="center-align">
--- a/templates/bundle/index.djhtml +++ b/templates/bundle/index.djhtml @@ -5,6 +5,10 @@ New bundle {% endblock title %} +{% block section %} +New bundle +{% endblock %} + {% block content %} <div id="non-sidebar"> <h1>{% trans "Upload a new bundle" %}</h1>
--- a/templates/simple_field.djhtml +++ b/templates/simple_field.djhtml @@ -1,10 +1,13 @@ -<strong>{{ field.label_tag }}</strong> -{{ field }} +<div class="form-field"> + <strong>{{ field.label_tag }}</strong> + + {% if field.errors %} + <div class="errors">{{ field.errors }}</div> + {% endif %} -{% if field.errors %} -<div class="errors">{{ field.errors }}</div> -{% endif %} + {{ field }} -{% if field.help_text %} -<p>{{ field.help_text }}</p> -{% endif %} + {% if field.help_text %} + <p>{{ field.help_text }}</p> + {% endif %} +</div>