Mercurial > public > sg101
changeset 285:8fd4984d5c3b
This is a first rough commit for #95, adding the ability to embed YouTube videos in forum posts. Some more polish and testing needs to happen at this point. I wanted to get all these changes off my hard drive and into the repository.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Thu, 14 Oct 2010 02:39:35 +0000 |
parents | df2c81f705a8 |
children | 72fd300685d5 |
files | gpp/forums/attachments.py gpp/forums/forms.py gpp/forums/models.py gpp/forums/templatetags/forum_tags.py gpp/forums/tools.py gpp/forums/urls.py gpp/forums/views/attachments.py gpp/forums/views/main.py gpp/oembed/__init__.py gpp/oembed/admin.py gpp/oembed/core.py gpp/oembed/models.py gpp/oembed/urls.py gpp/oembed/views.py gpp/settings.py gpp/templates/forums/display_post.html gpp/templates/forums/edit_post.html gpp/templates/forums/new_post.html gpp/templates/forums/new_topic.html gpp/templates/forums/show_form.html gpp/templates/forums/topic.html gpp/urls.py media/css/base.css media/icons/television_add.png media/icons/television_delete.png media/js/forums.js |
diffstat | 25 files changed, 578 insertions(+), 9 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/forums/attachments.py Thu Oct 14 02:39:35 2010 +0000 @@ -0,0 +1,45 @@ +""" +This module contains a class for handling attachments on forum posts. +""" +from oembed.models import Oembed +from forums.models import Attachment + + +class AttachmentProcessor(object): + """ + This class is aggregated by various form classes to handle + attachments on forum posts. New posts can receive attachments and edited + posts can have their attachments replaced, augmented, or deleted. + + """ + def __init__(self, ids): + """ + This class is constructed with a list of Oembed ids. We retrieve the + actual Oembed objects associated with these keys for use in subsequent + operations. + + """ + # ensure all ids are integers + self.pks = [] + for pk in ids: + try: + pk = int(pk) + except ValueError: + continue + self.pks.append(pk) + + self.embeds = [] + if self.pks: + self.embeds = Oembed.objects.in_bulk(self.pks) + + def save_attachments(self, post): + """ + Create and save attachments to the supplied post object. + Any existing attachments on the post are removed first. + + """ + post.attachments.clear() + + for n, pk in enumerate(self.pks): + attachment = Attachment(post=post, embed=self.embeds[pk], order=n) + attachment.save()
--- a/gpp/forums/forms.py Mon Oct 04 01:01:29 2010 +0000 +++ b/gpp/forums/forms.py Thu Oct 14 02:39:35 2010 +0000 @@ -7,6 +7,7 @@ from forums.models import Forum from forums.models import Topic from forums.models import Post +from forums.attachments import AttachmentProcessor class NewPostForm(forms.Form): @@ -25,6 +26,11 @@ settings.GPP_THIRD_PARTY_JS['jquery-ui'] + ('js/forums.js', )) + def __init__(self, *args, **kwargs): + super(NewPostForm, self).__init__(*args, **kwargs) + attachments = args[0].getlist('attachment') if len(args) else [] + self.attach_proc = AttachmentProcessor(attachments) + def clean_topic_id(self): id = self.cleaned_data['topic_id'] try: @@ -40,6 +46,7 @@ post = Post(topic=self.topic, user=user, body=self.cleaned_data['body'], user_ip=ip) post.save() + self.attach_proc.save_attachments(post) return post @@ -76,6 +83,9 @@ self.fields['locked'] = forms.BooleanField(required=False) self.has_mod_fields = True + attachments = args[0].getlist('attachment') if len(args) else [] + self.attach_proc = AttachmentProcessor(attachments) + def save(self, ip=None): """ Creates the new Topic and first Post from the form data and supplied @@ -93,6 +103,9 @@ body=self.cleaned_data['body'], user_ip=ip) post.save() + + self.attach_proc.save_attachments(post) + return topic
--- a/gpp/forums/models.py Mon Oct 04 01:01:29 2010 +0000 +++ b/gpp/forums/models.py Thu Oct 14 02:39:35 2010 +0000 @@ -8,6 +8,7 @@ from django.contrib.auth.models import User, Group from core.markup import site_markup +from oembed.models import Oembed class Category(models.Model): @@ -248,6 +249,7 @@ body = models.TextField() html = models.TextField() user_ip = models.IPAddressField(blank=True, default='', null=True) + attachments = models.ManyToManyField(Oembed, through='Attachment') class Meta: ordering = ('creation_date', ) @@ -365,3 +367,18 @@ def touch(self): self.last_visit = datetime.datetime.now() + +class Attachment(models.Model): + """ + This model is a "through" table for the M2M relationship between forum + posts and Oembed objects. + """ + post = models.ForeignKey(Post) + embed = models.ForeignKey(Oembed) + order = models.IntegerField() + + class Meta: + ordering = ('order', ) + + def __unicode__(self): + return u'Post %d, %s' % (self.post.pk, self.embed.title)
--- a/gpp/forums/templatetags/forum_tags.py Mon Oct 04 01:01:29 2010 +0000 +++ b/gpp/forums/templatetags/forum_tags.py Thu Oct 14 02:39:35 2010 +0000 @@ -100,16 +100,25 @@ @register.inclusion_tag('forums/show_form.html') -def show_form(legend_text, form, submit_value, is_ajax, media_url): +def show_form(legend_text, form, submit_value, is_ajax, post=None): """ This tag displays the common HTML for a forum form. + If post is not None, then we are editing an existing post. We must get the + post id into the template if the post has attachments. AJAX is used to + retrieve the attachments. """ + post_id = None + if post is not None: + if post.attachments.count() > 0: + post_id = post.id + return { 'legend_text': legend_text, 'form': form, 'submit_value': submit_value, 'is_ajax': is_ajax, - 'MEDIA_URL': media_url, + 'post_id': post_id, + 'MEDIA_URL': settings.MEDIA_URL, }
--- a/gpp/forums/tools.py Mon Oct 04 01:01:29 2010 +0000 +++ b/gpp/forums/tools.py Thu Oct 14 02:39:35 2010 +0000 @@ -15,6 +15,10 @@ """ posts = Post.objects.filter(user=user).select_related() + # delete attachments + for post in posts: + post.attachments.clear() + # build a set of topics and forums affected by the post deletions topics = set(post.topic for post in posts)
--- a/gpp/forums/urls.py Mon Oct 04 01:01:29 2010 +0000 +++ b/gpp/forums/urls.py Thu Oct 14 02:39:35 2010 +0000 @@ -48,3 +48,7 @@ url(r'^spammer/nailed/(\d+)/$', 'spammer_nailed', name='forums-spammer_nailed'), url(r'^stranger/(\d+)/$', 'stranger', name='forums-stranger'), ) + +urlpatterns += patterns('forums.views.attachments', + url(r'^fetch_attachments/$', 'fetch_attachments', name='forums-fetch_attachments'), +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/forums/views/attachments.py Thu Oct 14 02:39:35 2010 +0000 @@ -0,0 +1,35 @@ +""" +This module contains views for working with post attachments. +""" +from django.http import HttpResponse +from django.http import HttpResponseForbidden +from django.http import HttpResponseBadRequest +from django.http import HttpResponseNotFound +import django.utils.simplejson as json + +from forums.models import Post + + +def fetch_attachments(request): + """ + This view is the target of an AJAX GET request to retrieve the + attachment embed data for a given forum post. + + """ + if not request.user.is_authenticated(): + return HttpResponseForbidden('Please login or register.') + + post_id = request.GET.get('pid') + if post_id is None: + return HttpResponseBadRequest('Missing post ID.') + + try: + post = Post.objects.get(pk=post_id) + except Post.DoesNotExist: + return HttpResponseNotFound("That post doesn't exist.") + + embeds = post.attachments.all().select_related('embed') + data = [{'id': embed.id, 'html': embed.html} for embed in embeds] + + return HttpResponse(json.dumps(data), content_type='application/json') +
--- a/gpp/forums/views/main.py Mon Oct 04 01:01:29 2010 +0000 +++ b/gpp/forums/views/main.py Thu Oct 14 02:39:35 2010 +0000 @@ -23,7 +23,7 @@ from core.paginator import DiggPaginator from core.functions import email_admins from forums.models import Forum, Topic, Post, FlaggedPost, TopicLastVisit, \ - ForumLastVisit + ForumLastVisit, Attachment from forums.forms import NewTopicForm, NewPostForm, PostForm, MoveTopicForm, \ SplitTopicForm from forums.unread import get_forum_unread_status, get_topic_unread_status, \ @@ -32,6 +32,7 @@ from bio.models import UserProfile import antispam import antispam.utils +from forums.attachments import AttachmentProcessor ####################################################################### @@ -184,6 +185,16 @@ for post in page.object_list: post.user_profile = user_profiles[post.user.id] + post.attach_list = [] + + # Attach any attachments + post_ids = [post.pk for post in page.object_list] + attachments = Attachment.objects.filter(post__in=post_ids).select_related( + 'embed').order_by('order') + + post_dict = dict((post.pk, post) for post in page.object_list) + for item in attachments: + post_dict[item.post.id].attach_list.append(item.embed) last_page = page_num == paginator.num_pages @@ -283,8 +294,10 @@ post = form.save(request.user, request.META.get("REMOTE_ADDR", "")) post.unread = True post.user_profile = request.user.get_profile() + post.attach_list = post.attachments.all() _bump_post_count(request.user) _update_last_visit(request.user, form.topic) + return render_to_response('forums/display_post.html', { 'post': post, 'can_moderate': _can_moderate(form.topic.forum, request.user), @@ -359,6 +372,11 @@ post = form.save(commit=False) post.touch() post.save() + + # Save any attachments + attach_proc = AttachmentProcessor(request.POST.getlist('attachment')) + attach_proc.save_attachments(post) + return HttpResponseRedirect(post.get_absolute_url()) else: form = PostForm(instance=post) @@ -439,6 +457,9 @@ forum.last_post_pre_delete() forum.save() + # delete any attachments + post.attachments.clear() + # Should be safe to delete the post now: post.delete() @@ -459,6 +480,11 @@ topic.subscribers.clear() topic.bookmarkers.clear() + # delete all attachments + posts = Post.objects.filter(topic=topic) + for post in posts: + post.attachments.clear() + # It should be safe to just delete the topic now. This will # automatically delete all posts in the topic. topic.delete() @@ -484,6 +510,11 @@ post.user = request.user post.user_ip = request.META.get("REMOTE_ADDR", "") post.save() + + # Save any attachments + attach_proc = AttachmentProcessor(request.POST.getlist('attachment')) + attach_proc.save_attachments(post) + _bump_post_count(request.user) _update_last_visit(request.user, topic) return HttpResponseRedirect(post.get_absolute_url())
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/oembed/admin.py Thu Oct 14 02:39:35 2010 +0000 @@ -0,0 +1,24 @@ +""" +Admin site definitions for the oembed application. +""" +from django.contrib import admin + +from oembed.models import Provider +from oembed.models import Oembed + + +class ProviderAdmin(admin.ModelAdmin): + list_display = ('name', 'api_endpoint', 'format') + list_filter = ('format', ) + search_fields = ('name', ) + + +class OembedAdmin(admin.ModelAdmin): + date_hierarchy = 'date_added' + list_display = ('__unicode__', 'type', 'url', 'date_added') + list_filter = ('type', ) + search_fields = ('title', ) + + +admin.site.register(Provider, ProviderAdmin) +admin.site.register(Oembed, OembedAdmin)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/oembed/core.py Thu Oct 14 02:39:35 2010 +0000 @@ -0,0 +1,57 @@ +""" +This module contains core functionality for the oembed application. +""" +from __future__ import with_statement +import urllib +import urllib2 +import gzip +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + +import django.utils.simplejson as json + + +USER_AGENT = 'gremmies python oembed' + + +def get_oembed(api_endpoint, url, format='json', **opts): + """ + Perform the GET request to retrieve the embedded media data from the given + API endpoint for the given URL. Return the result as a Python dictionary. + + format specifies the response format, and should be 'json' or 'xml'. + opts are any additional GET options that should be present in the GET + request. + + """ + opts['url'] = url + opts['format'] = format + api_url = "%s?%s" % (api_endpoint, urllib.urlencode(opts)) + + headers = { + 'User-Agent': USER_AGENT, + 'Accept-Encoding': 'gzip', + } + request = urllib2.Request(api_url, headers=headers) + + opener = urllib2.build_opener() + f = opener.open(request) + headers = f.info() + result = f.read() + f.close() + + if headers.get('content-encoding') == 'gzip': + with gzip.GzipFile(fileobj=StringIO(result)) as f: + result = f.read() + + return json.loads(result) + +if __name__ == "__main__": + try: + print get_oembed("http://www.youtube.com/oembed", + #"http://www.youtube.com/watch?v=7_IMzJldOf4") + "http://www.youtube.com/watch?v=99999999999") + except urllib2.HTTPError, e: + print e
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/oembed/models.py Thu Oct 14 02:39:35 2010 +0000 @@ -0,0 +1,58 @@ +""" +Models for the oembed application. +""" +import datetime + +from django.db import models + + +class Provider(models.Model): + """ + This model described an oEmbed provider. + """ + JSON, XML = range(2) + FORMAT_CHOICES = ( + (JSON, "json"), + (XML, "xml"), + ) + + name = models.CharField(max_length=128) + api_endpoint = models.URLField(max_length=255, verify_exists=False, + verbose_name='API endpoint') + url_regex = models.CharField(max_length=255, verbose_name='URL regex') + format = models.IntegerField(choices=FORMAT_CHOICES) + + def __unicode__(self): + return self.name + + +class Oembed(models.Model): + """ + This model represents stored embedded content retrieved from an oEmbed + provider. + """ + PHOTO, VIDEO, LINK, RICH = range(4) + MEDIA_TYPE_CHOICES = ( + (PHOTO, "photo"), + (VIDEO, "video"), + (LINK, "link"), + (RICH, "rich"), + ) + + url = models.URLField(max_length=255, verify_exists=False, db_index=True) + type = models.IntegerField(choices=MEDIA_TYPE_CHOICES) + title = models.CharField(max_length=255, blank=True, default='') + width = models.IntegerField() + height = models.IntegerField() + html = models.TextField() + date_added = models.DateTimeField() + + def __unicode__(self): + return self.title or self.url + + def save(self, *args, **kwargs): + if not self.pk: + self.date_added = datetime.datetime.now() + + super(Oembed, self).save(*args, **kwargs) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/oembed/urls.py Thu Oct 14 02:39:35 2010 +0000 @@ -0,0 +1,8 @@ +""" +URLs for the oembed application. +""" +from django.conf.urls.defaults import * + +urlpatterns = patterns('oembed.views', + url(r'^fetch/$', 'fetch_media', name='oembed-fetch_media'), +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gpp/oembed/views.py Thu Oct 14 02:39:35 2010 +0000 @@ -0,0 +1,65 @@ +""" +Views for the oembed application. +""" +import re +import urllib2 + +from django.http import HttpResponse +from django.http import HttpResponseBadRequest +from django.http import HttpResponseForbidden +import django.utils.simplejson as json + +from oembed.models import Provider +from oembed.models import Oembed +from oembed.core import get_oembed + + +def fetch_media(request): + """ + This view returns the HTML media of an embeddable resource. + This view is the target of an AJAX request. + """ + if not request.user.is_authenticated(): + return HttpResponseForbidden('Please login or register.') + + url = request.POST.get('q') + + if not url: + return HttpResponseBadRequest('Please provide a valid URL.') + + # Is this already in our database? + try: + oembed = Oembed.objects.get(url=url) + except Oembed.DoesNotExist: + pass + else: + data = dict(id=oembed.id, embed=oembed.html) + return HttpResponse(json.dumps(data), content_type='application/json') + + # It isn't in the database, try to find it from our providers + providers = Provider.objects.all() + for provider in providers: + if re.match(provider.url_regex, url): + try: + data = get_oembed(provider.api_endpoint, url) + except IOError, e: + return HttpResponseBadRequest( + "Sorry, we could not retrieve your video (%s)" % e) + + if 'type' not in data or data['type'] != 'video': + return HttpResponseBadRequest( + "Hey, this doesn't look like a video..??") + + oembed = Oembed(url=url, + type=Oembed.VIDEO, + title=data.get('title', ''), + width=int(data.get('width', 0)), + height=int(data.get('height', 0)), + html=data.get('html', '')) + oembed.save() + + data = dict(id=oembed.id, embed=oembed.html) + return HttpResponse(json.dumps(data), + content_type='application/json') + + return HttpBadRequest("Sorry, we couldn't find that video.")
--- a/gpp/settings.py Mon Oct 04 01:01:29 2010 +0000 +++ b/gpp/settings.py Thu Oct 14 02:39:35 2010 +0000 @@ -144,6 +144,7 @@ 'membermap', 'messages', 'news', + 'oembed', 'podcast', 'polls', 'potd',
--- a/gpp/templates/forums/display_post.html Mon Oct 04 01:01:29 2010 +0000 +++ b/gpp/templates/forums/display_post.html Thu Oct 14 02:39:35 2010 +0000 @@ -38,6 +38,13 @@ <p class="small quiet">Last edited: {{ post.update_date|date:"M d, Y H:i:s" }}</p> {% endif %} </div> + {% if post.attach_list %} + <div> + {% for item in post.attach_list %} + <div class="forum-attachment">{{ item.html|safe }}</div> + {% endfor %} + </div> + {% endif %} <div class="forum-post-info-tools"> {% if can_reply %} <a href="{% url forums-new_post topic_id=post.topic.id %}?quote={{ post.id }}"><img src="{{ MEDIA_URL }}icons/comment.png" alt="Reply with quote" title="Reply with quote" /></a>
--- a/gpp/templates/forums/edit_post.html Mon Oct 04 01:01:29 2010 +0000 +++ b/gpp/templates/forums/edit_post.html Thu Oct 14 02:39:35 2010 +0000 @@ -11,6 +11,6 @@ </table> <a name="forum-reply-form"></a> -{% show_form "Edit Post" form "Update Post" 0 MEDIA_URL %} +{% show_form "Edit Post" form "Update Post" 0 post %} </div> {% endblock %}
--- a/gpp/templates/forums/new_post.html Mon Oct 04 01:01:29 2010 +0000 +++ b/gpp/templates/forums/new_post.html Thu Oct 14 02:39:35 2010 +0000 @@ -8,7 +8,7 @@ {% if can_post %} <div class="forum-block"> <a name="forum-reply-form"></a> -{% show_form "New Post" form "Submit Post" 0 MEDIA_URL %} +{% show_form "New Post" form "Submit Post" 0 %} {% else %} {% if topic.locked %} <p>This topic is locked.</p>
--- a/gpp/templates/forums/new_topic.html Mon Oct 04 01:01:29 2010 +0000 +++ b/gpp/templates/forums/new_topic.html Thu Oct 14 02:39:35 2010 +0000 @@ -5,5 +5,5 @@ {% block content %} {% forum_navigation forum "New Topic" %} -{% show_form "New Topic" form "Submit" 0 MEDIA_URL %} +{% show_form "New Topic" form "Submit" 0 %} {% endblock %}
--- a/gpp/templates/forums/show_form.html Mon Oct 04 01:01:29 2010 +0000 +++ b/gpp/templates/forums/show_form.html Thu Oct 14 02:39:35 2010 +0000 @@ -5,5 +5,12 @@ {{ form.as_p }} {% comment_dialogs %} <input type="submit" value="{{ submit_value }}" {% if is_ajax %}id="forums-reply-post"{% endif %} /> + +<br /> +<br /> +<div id="attachment"> +{% if post_id %}<input type="hidden" name="post_id" value="{{ post_id }}" />{% endif %} +</div> + </fieldset> </form>
--- a/gpp/templates/forums/topic.html Mon Oct 04 01:01:29 2010 +0000 +++ b/gpp/templates/forums/topic.html Thu Oct 14 02:39:35 2010 +0000 @@ -49,7 +49,7 @@ {% if last_page and can_reply %} <a name="forum-reply-form"></a> -{% show_form "Reply to Topic" form "Submit Reply" 1 MEDIA_URL %} +{% show_form "Reply to Topic" form "Submit Reply" 1 %} {% endif %} {% if user.is_authenticated %}
--- a/gpp/urls.py Mon Oct 04 01:01:29 2010 +0000 +++ b/gpp/urls.py Thu Oct 14 02:39:35 2010 +0000 @@ -39,6 +39,7 @@ (r'^member_map/', include('membermap.urls')), (r'^messages/', include('messages.urls')), (r'^news/', include('news.urls')), + (r'^oembed/', include('oembed.urls')), (r'^podcast/', include('podcast.urls')), (r'^polls/', include('polls.urls')), (r'^potd/', include('potd.urls')),
--- a/media/css/base.css Mon Oct 04 01:01:29 2010 +0000 +++ b/media/css/base.css Thu Oct 14 02:39:35 2010 +0000 @@ -384,3 +384,12 @@ } h2.forum-nav {font-size:1.2em;margin-top:1em;margin-bottom:0.2em;} h3.forum-nav {font-size:2em;line-height:1;margin-bottom:1em;margin-left:1em;} +#attachment div { + margin: 1.0em 1.5em; +} +#attachment div span.link { + margin: 1em; +} +div.forum-attachment { + margin: 1.0em 1.5em; +}
--- a/media/js/forums.js Mon Oct 04 01:01:29 2010 +0000 +++ b/media/js/forums.js Thu Oct 14 02:39:35 2010 +0000 @@ -8,13 +8,21 @@ return false; } $(this).attr('disabled', 'disabled').val('Posting reply...'); + + var attachments = new Array() + $('#attachment div input').each(function(index) { + attachments[index] = $(this).val(); + }); + $.ajax({ url: '/forums/quick-reply/', type: 'POST', data: { - body : postText.val(), - topic_id : $('#id_topic_id').val() + body : postText.val(), + topic_id : $('#id_topic_id').val(), + attachment : attachments }, + traditional: true, dataType: 'html', success: function (data, textStatus) { postText.val(''); @@ -23,11 +31,13 @@ lastTr.hide(); lastTr.fadeIn(3000); postButton.removeAttr('disabled').val('Submit Reply'); + initAttachments(); }, error: function (xhr, textStatus, ex) { alert('Oops, an error occurred. ' + xhr.statusText + ' - ' + xhr.responseText); postButton.removeAttr('disabled').val('Submit Reply'); + initAttachments(); } }); return false; @@ -82,4 +92,168 @@ return confirm('Are you sure you want to delete this topic?\n' + 'WARNING: all posts will be lost.'); }); + + var vid = 0; + var vidDiv = $('#attachment'); + + function clearAttachments() + { + $('#attachment div').remove(); + $('#attach-another').remove(); + } + + function initAttachments() + { + clearAttachments(); + + var post_input = $('#attachment input'); + if (post_input.length == 1) + { + post_id = post_input.val(); + post_input.replaceWith('<img src="/media/icons/ajax_busy.gif" alt="Busy" />'); + $.ajax({ + url: '/forums/fetch_attachments/', + type: 'GET', + data: { + pid : post_id + }, + dataType: 'json', + success: function (data, textStatus) { + $('#attachment img').remove(); + $.each(data, function(index, value) { + var html = '<div id="video-' + index + '">' + value.html + + '<span class="link">' + + '<img src="/media/icons/television_delete.png" alt="Remove" /> ' + + '<a href="#">Remove</a></span>' + + '<input type="hidden" name="attachment" value="' + value.id + '" />'; + '</div>'; + vidDiv.append(html); + $('#video-' + index + ' a').click(function() { + $('#video-' + index).remove(); + relabelAttachLink(); + return false; + }); + }); + vid = data.length; + $('#video-' + (vid-1)).after('<a id="attach-another" href="#">Attach another video</a>'); + $('#attach-another').click(function() { + addVideo(); + relabelAttachLink(); + return false; + }); + }, + error: function (xhr, textStatus, ex) { + alert('Oops, an error occurred. ' + xhr.statusText + ' - ' + + xhr.responseText); + } + }); + } + else + { + vid = 0; + var s = '<div id="init-add">' + + '<img src="/media/icons/television_add.png" alt="Add" /> ' + + '<a href="#">Attach Video</a></div>'; + vidDiv.prepend(s); + $('#attachment a').click(function () { + $('#init-add').remove(); + addVideo(); + return false; + }); + } + } + + function relabelAttachLink() + { + var another = $('#attach-another'); + var n = $('#attachment div').length; + if (n == 0) + { + another.html("Attach a video"); + } + else + { + another.html("Attach another video"); + } + } + + function addVideo() + { + var id = "video-" + vid; + + var fakeForm = '<div id="' + id + '">' + + '<img src="/media/icons/television_add.png" alt="Attach" class="r" /> ' + + '<input type="text" size="45" class="r" /> <button type="button" class="r">Attach</button> ' + + '<a href="#" class="r">Remove</a><br /></div>'; + + var n = $('#attachment div').length; + + var another = $('#attach-another'); + if (n == 0) + { + if (another.length > 0) + { + another.before(fakeForm); + } + else + { + vidDiv.append(fakeForm); + } + } + else + { + $('#attachment div:last').after(fakeForm); + } + + $('#' + id + ' a').click(function() { + $('#' + id).remove(); + relabelAttachLink(); + return false; + }); + + var vidText = $('#' + id + ' input'); + + $('#' + id + ' button').click(function() { + $.ajax({ + url: '/oembed/fetch/', + type: 'POST', + data: { + q : vidText.val() + }, + dataType: 'json', + success: function (data, textStatus) { + $('#' + id + " .r").remove(); + var myDiv = $('#' + id); + var html = '<span class="link">' + + '<img src="/media/icons/television_delete.png" alt="Remove" /> ' + + '<a href="#">Remove</a></span>' + + '<input type="hidden" name="attachment" value="' + data.id + '" />'; + myDiv.prepend(html); + myDiv.prepend(data.embed); + $('#' + id + ' a').click(function() { + myDiv.remove(); + relabelAttachLink(); + return false; + }); + }, + error: function (xhr, textStatus, ex) { + alert('Oops, an error occurred. ' + xhr.statusText + ' - ' + + xhr.responseText); + } + }); + }); + + if (vid == 0) + { + $('#video-0').after('<a id="attach-another" href="#">Attach another video</a>'); + $('#attach-another').click(function() { + addVideo(); + relabelAttachLink(); + return false; + }); + } + ++vid; + } + + initAttachments(); });