"""myEWB GroupTopics views
 
This file is part of myEWB
Copyright 2009 Engineers Without Borders (Canada) Organisation and/or volunteer contributors
Some code derived from Pinax, copyright 2008-2009 James Tauber and Pinax Team, licensed under the MIT License
 
Created on: 2009-08-13
Last modified: 2009-12-02
@author: Joshua Gorner, Francis Kung
"""
 
import settings
from time import time
 
from django.http import HttpResponse, HttpResponseForbidden, HttpResponseRedirect, Http404
from django.utils.translation import ugettext as _
from django.utils.html import escape
from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404, render_to_response
from django.core.exceptions import ObjectDoesNotExist
from django.core.files.uploadhandler import TemporaryFileUploadHandler
from django.core.urlresolvers import reverse
from django.template import RequestContext
from django.db.models import Q
from emailconfirmation.models import EmailAddress
 
from groups import bridge
 
from account_extra.forms import EmailLoginForm
from base_groups.models import BaseGroup
from base_groups.helpers import user_can_adminovision, user_can_execovision
from group_topics.models import GroupTopic, Watchlist
from group_topics.forms import GroupTopicForm
from threadedcomments.models import ThreadedComment
from profiles.models import MemberProfile
from siteutils.shortcuts import get_object_or_none
 
from attachments.forms import AttachmentForm
from attachments.models import Attachment
from pinax.apps.topics.models import Topic
from whiteboard.models import Whiteboard
 
def topic(request, topic_id, group_slug=None, edit=False, template_name="topics/topic.html", bridge=None):
 
    topic = get_object_or_404(GroupTopic, id=topic_id)
 
    parent_group = topic.parent_group
    # XXX PERMISSIONS CHECK
    if not parent_group.is_visible(request.user) and not topic.creator == request.user:
        return render_to_response("topics/disallowed.html", {
            "topic": None,
            "group": parent_group,
            "member": None,
            "grpadmin": None,
        }, context_instance=RequestContext(request))
 
 
    # XXX PERMISSIONS CHECK
    # only the owner of a topic or a group admin can edit a topic (??)
    if (request.method == "POST" and edit == True and \
            topic.is_editable(request.user)):
        updated_body = request.POST.get('body', None)
        if updated_body is not None:
            topic.body = updated_body
            topic.save()
        return HttpResponseRedirect(topic.get_absolute_url())
 
    # retrieve whiteboard (create if needed)
    if topic.whiteboard == None:
        # group_slug should always be valid - group never null!
        wb = Whiteboard(title="Post%d" % (topic.id), content="")
        if topic.group:
            topic.group.associate(wb, commit=False)
        wb.save()
        topic.whiteboard = wb
        topic.save()
 
    # update "featured posts" score
    topic.update_score(settings.FEATURED_VIEW_SCORE)
 
    # find membership status
    member = False
    if request.user.is_authenticated() and topic.group and (topic.group.user_is_member(request.user) or topic.group.slug == "ewb"):
        member = True
 
    grpadmin = topic.group.user_is_admin(request.user)
 
 
    return render_to_response(template_name, {
        "topic": topic,
        "group": topic.group,
        "member": member,
        "grpadmin": grpadmin,
    }, context_instance=RequestContext(request))
 
# if group_slug=None will return all visible posts; otherwise will restrict by group
# if featured=True will sort by score; otherwise will sort by date
def topics(request, group_slug=None, form_class=GroupTopicForm,
           attach_form_class=AttachmentForm, template_name="topics/topics.html",
           bridge=None, mode=None):
 
    if request.is_ajax() or request.GET.get('ajax', None):
        template_name = "topics/topics_ajax.html"
 
    # kinda hack-ish.  but the easiest way; doesn't feel worth adding an AJAX param for this.
    if group_slug == 'featured':
        mode = 'featured'
        group_slug = None
 
    is_member = False
    group = None
    if group_slug is not None:
        group = get_object_or_404(BaseGroup, slug=group_slug)
        is_member = group.user_is_member(request.user, admin_override=True)
 
    if group and not group.is_visible(request.user):
        return HttpResponseForbidden()
 
    attach_count = 0
    if request.method == "POST" and group:
        return new_topic(request, group_slug)
    else:
        topic_form = form_class(instance=GroupTopic(), user=request.user, group=group)
 
        attach_forms = []
 
    # if it's a listing by group, check group visibility
    if group:
        topics = GroupTopic.objects.get_for_group(group)
 
    # otherwise throw up a generic listing of visible posts
    else:
        # generic topic listing: show posts from groups you're in
        # also shows posts from public groups...
        # for guests, show posts from public groups only
        topics = GroupTopic.objects.visible(user=request.user)
 
    if mode == 'featured':
        topics = GroupTopic.objects.featured(topics)
    elif mode == 'newposts' and request.user.is_authenticated():
        topics = GroupTopic.objects.since(request.user.get_profile().previous_login, qs=topics)
    elif mode == 'newreplies' and request.user.is_authenticated():
        topics = GroupTopic.objects.replies_since(request.user.get_profile().previous_login, qs=topics)
 
    if request.user.is_authenticated():
        can_adminovision = user_can_adminovision(request.user)
        can_execovision = user_can_execovision(request.user)
        adminovision = request.user.get_profile().adminovision
 
        if not request.user.get_profile().show_emails:
            topics = GroupTopic.objects.exclude_emails(topics)
 
    else:
        can_adminovision = False
        can_execovision = False
        adminovision = False
 
    # also kinda hackish
    if group and group.slug == "ewb":
        group = None
        mode = "frontpage"
    elif not group and not mode:
        mode = "latest" 
 
    return render_to_response(template_name, {
        "group": group,
        "topic_form": topic_form,
        "attach_forms": attach_forms,
        "attach_count": attach_count,
        "is_member": is_member,
        "topics": topics,
        "can_adminovision": can_adminovision,
        "can_execovision": can_execovision,
        "adminovision": adminovision,
        "login_form": EmailLoginForm(),                # for front-page toolbar
        "mode": mode
    }, context_instance=RequestContext(request))
 
def new_topic(request, group_slug=None, bridge=None):
    is_member = False
    group = None
    if group_slug is None:
        group_slug = "ewb"
 
    group = get_object_or_404(BaseGroup, slug=group_slug)
    is_member = group.user_is_member(request.user, admin_override=True)
 
    if not group.is_visible(request.user):
        return HttpResponseForbidden()
 
    attach_count = 0
    if request.method == "POST":
        if not request.user.is_authenticated():
            return HttpResponseForbidden()
        try:
            attach_count = int(request.POST.get("attach_count", 0))
        except ValueError:
            attach_count = 0
 
        if group.slug == "ewb" or is_member:
            # has been previewed.  mark it as good to go!
            if request.POST.get("previewed", None) and request.POST.get("postid", None):
                topic = GroupTopic.objects.get(id=request.POST['postid'], creator=request.user)
                if topic.pending:
                    topic.pending = False
                    topic.save()
 
                    # extra security check that sender isn't forged.
                    # can't hurt...
                    sender_valid = False
                    if group.user_is_admin(request.user) and request.POST.get('sender', None):
                        if request.POST['sender'] == group.from_email:
                            sender_valid = True
                            sender = '"%s" <%s>' % (group.from_name, group.from_email)
 
                        elif get_object_or_none(EmailAddress, email=request.POST['sender']) in request.user.get_profile().email_addresses():
                            sender_valid = True
                            sender = '"%s %s" <%s>' % (request.user.get_profile().first_name,
                                                       request.user.get_profile().last_name,
                                                       request.POST['sender'])
 
                        elif request.user.is_staff and request.POST['sender'] == "info@ewb.ca":
                            sender_valid = True
                            sender = '"EWB-ISF Canada" <info@ewb.ca>'
 
                    if topic.send_as_email:
                        if sender_valid:
                            request.user.message_set.create(message=escape("Sent as %s" % sender))
                            topic.send_email(sender=sender)
                        else:
                            request.user.message_set.create(message="Unable to send email.")
 
                    # redirect out.
                    request.user.message_set.create(message=_("You have started the topic %(topic_title)s") % {"topic_title": topic.title})
 
                else:
                    # probably double-clicked the submit button and this is the dupe request... 
                    pass
 
                return HttpResponseRedirect(topic.get_absolute_url())
 
            # confirmation was cancelled, so delete the temp post and bump back to edit screen
            topic = None
            if request.POST.get("goback", None) and request.POST.get("postid", None):
                topic = get_object_or_none(GroupTopic, id=request.POST['postid'], pending=True, creator=request.user)
 
            if topic: 
                topic_form = GroupTopicForm(instance=topic, user=request.user, group=group)
                attach_forms = []
                topic.delete()
 
            # validate form and show preview...
            else:
                topic_form = GroupTopicForm(request.POST, user=request.user, group=group)
                attach_forms = [AttachmentForm(request.POST, request.FILES, prefix=str(x), instance=Attachment()) for x in range(0,attach_count)]
 
                # do not take blank attachment forms into account
                for af in attach_forms:
                    if not af.is_valid() and not af['attachment_file'].data:
                        attach_forms.remove(af)
                        attach_count = attach_count - 1
 
                # all good.  save it!
                if topic_form.is_valid() and all([af.is_valid() for af in attach_forms]) and not request.POST.get("goback", None):
                    # save the post but mark it as "pending".... and display a confirmation.
                    topic = topic_form.save(commit=False)
                    if group:
                        group.associate(topic, commit=False)
                    topic.creator = request.user
                    topic.pending = True
                    topic.save()
 
                    # save the attachments.
                    # We need the "Topic" object in order to retrieve attachments properly
                    # since other functions only get the Topic object
                    base_topic = GroupTopic.objects.get(id=topic.id)
                    attachments = []
                    for af in attach_forms:
                        attachment = af.save(request, base_topic)
                        attachments.append(af.cleaned_data['attachment_file'].name)
 
                    sender = None
                    if topic_form.cleaned_data.get('send_as_email', None):
                        sender = topic_form.cleaned_data.get('sender', None)
 
                    is_large_group = False
                    if group.members.count() > 50:
                        is_large_group = True
 
                    return render_to_response("topics/preview.html",
                                              {"group": group,
                                               "topic": topic,
                                               "is_member": is_member,
                                               "sender": sender,
                                               "attachments": attachments,
                                               "is_large_group": is_large_group,
                                              },
                                              context_instance=RequestContext(request))
        else:
            # if they can't start a topic, why are we still loading up a form?
            request.user.message_set.create(message=_("You are not a member and so cannot start a new topic"))
            topic_form = GroupTopicForm(instance=GroupTopic())                
            attach_forms = [AttachmentForm(prefix=str(x), instance=Attachment()) for x in range(0,attach_count)]
 
    else:
        topic_form = GroupTopicForm(instance=GroupTopic(), user=request.user, group=group)
        attach_forms = []
 
    return render_to_response("topics/new_topic.html", {
        "group": group,
        "topic_form": topic_form,
        "attach_forms": attach_forms,
        "attach_count": attach_count,
        "is_member": is_member,
    }, context_instance=RequestContext(request))
 
def get_attachment_form(request, template_name="topics/attachment_form.html", form_class=AttachmentForm, group_slug=None, bridge=None):
 
    if request.is_ajax():
        attach_form = form_class(prefix=request.POST['prefix'], instance=Attachment())
        response = render_to_response(
            template_name,
            {
                'attach_form': attach_form,
            },
            context_instance=RequestContext(request),
        )
        return response
    else:
        raise Http404
 
def topic_delete(request, topic_id, group_slug=None, bridge=None, confirm=False):
    """
    Another copy-pasta from pinax.apps.topics.views.
    Again need to update to match our GroupTopic stuff.
    Mostly we 
    """
    if bridge:
        try:
            group = bridge.get_group(group_slug)
        except ObjectDoesNotExist:
            raise Http404
    else:
        group = None
 
    if group:
        topics = group.content_objects(Topic)
    else:
        # line below is the only change between pinax and us.
        # topics = Topic.objects.filter(object_id=None)
        topics = Topic.objects.all()
 
 
    topic = get_object_or_404(topics, id=topic_id)
 
    if (request.method == "POST" and (request.user == topic.creator or topic.group.user_is_admin(request.user))):
        if confirm:
            ThreadedComment.objects.all_for_object(topic).delete()
            topic.delete()
 
            return HttpResponseRedirect(request.POST["next"])
        else:
            return render_to_response("topics/topic_delete.html",
                                      {"topic": topic,
                                       "next": request.POST["next"]},
                                      context_instance=RequestContext(request)
                                     )
    else:
        return HttpResponseForbidden()
 
def topics_by_user(request, username):
    """
    Return a listing of all topics visible the current user, and created by
    the given user
    """
    user = get_object_or_404(User, username=username)
 
    if request.user == user:
        # user can always see their own post, regardless of group visibility
        # (ie, if I write some posts to a private group then leave the group, 
        #  those posts should still show in this listing)
        topics = GroupTopic.objects.get_for_user(user)
 
    else:
        # start with all visible topics
        topics = GroupTopic.objects.visible(request.user)
 
        # then restrict further to only ones by the given user
        topics = GroupTopic.objects.get_for_user(user, topics)
 
    return render_to_response("topics/topics.html",
                              {"topics": topics,
                               "group": None,
                               "mode": "byuser-%s" % username,
                               "hideheader": True},
                              context_instance=RequestContext(request)
                             )
 
def adminovision_toggle(request, group_slug=None):
    """
    Toggles admin-o-vision for the current user.
    No effect if user is not an admin
    """
 
    if user_can_adminovision(request.user) | user_can_execovision(request.user):
        profile = request.user.get_profile()
 
        profile.adminovision = not profile.adminovision
        profile.save()
 
    # this redirect should be OK, since the adminovision link is only visible from reverse('home')
    return HttpResponseRedirect(reverse('home'))
 
def watchlist(request, list_id):
    """
    Displays post listing in the given watchlist
    """
    list = get_object_or_404(Watchlist, pk=list_id)
 
    topics = GroupTopic.objects.get_for_watchlist(list)
 
    return render_to_response("topics/topics.html",
                              {"topics": topics,
                               "group": None,
                               "mode": "watchlist-%s" % list_id,
                               "hideheader": True},
                              context_instance=RequestContext(request)
                             )
 
def watchlist_index(request):
    """
    Displays list of watchlists belonging to user
    """
 
    # TODO: this will all change when we allow multiple lists per user...
    list, created = Watchlist.objects.get_or_create(owner=request.user,
                                                    defaults={'name': 'watchlist'})
    return watchlist(request, list.pk)
 
#def add_to_watchlist(request, list_id, topic_id):
def add_to_watchlist(request, user_id, topic_id):
    """
    Adds the specified topic to the watchlist.
    Meant to be an AJAX call.
    """
    #list = get_object_or_404(Watchlist, pk=list_id)
    user = get_object_or_404(User, pk=user_id)
    list, created = Watchlist.objects.get_or_create(owner=user,
                                                    defaults={'name': 'watchlist'})
    topic = get_object_or_404(GroupTopic, pk=topic_id)
 
    if list.user_can_control(request.user):
        list.add_post(topic)
        request.session['cache_stamp'] = time()
 
        # TODO: do I want to templatize?
        return HttpResponse("[remove from watch-list]")
    else:
        return HttpResponse("error! =(")
 
def remove_from_watchlist(request, user_id, topic_id):
    """
    Removes the specified topic from the watchlist.
    Meant to be an AJAX call.
    """
    #list = get_object_or_404(Watchlist, pk=list_id)
    user = get_object_or_404(User, pk=user_id)
    list = get_object_or_404(Watchlist, owner=user)
    topic = get_object_or_404(GroupTopic, pk=topic_id)
 
    if list.user_can_control(request.user):
        list.remove_post(topic)
        request.session['cache_stamp'] = time()
 
        # TODO: do I want to templatize?  do you!  just kidding. -sean  @@@
        return HttpResponse("[add to watch-list]")  # was "removed"
    else:
        return HttpResponse("error! =(")
 
def update_modifier(request, topic_id):
    """
    Updates the "featured post" score modifier - admins can use this to 
    bump specific posts...
    """
    if request.user.has_module_perms("group_topics") and request.method == 'POST':
        value = request.POST.get("value", "")
 
        try:
            modifier = int(value)
        except:
            return HttpResponse("invalid number")
 
        topic = get_object_or_404(GroupTopic, pk=topic_id)
        topic.update_modifier(modifier)
 
        return HttpResponse(topic.score)
    else:
        return HttpResponse("denied")