class Member(models.Model):
    GROUP_CHOICES = (
        (u'graduate', u'Graduate'),
        (u'undergraduate', u'Undergraduate'),
        (u'faculty', u'Faculty')
    )

    CLASS_CHOICES = (
        (u'freshman', u'Freshman'),
        (u'sophomore', u'Sophomore'),
        (u'junior', u'Junior'),
        (u'senior', u'Senior')
    )

    STATUS_EMPTY    = 0 # this member is pending "creation" by its owner
    STATUS_ACTIVE   = 1 # this member has been created
    STATUS_ARCHIVED = 2 # this member has been archived and is frozen

    STATUS_CHOICES  = (
        (STATUS_EMPTY, u'Empty'),
        (STATUS_ACTIVE, u'Active'),
        (STATUS_ARCHIVED, u'Archived')
    )

    user            = models.ForeignKey(User, related_name='profile', unique=True)
    group           = models.CharField(max_length=255, choices=GROUP_CHOICES)
    classification  = models.CharField(max_length=255, choices=CLASS_CHOICES, blank=True)
    hometown        = models.CharField(max_length=255, blank=True)
    interests       = models.TextField(blank=True)
    homepage        = models.URLField(blank=True)
    blurb           = models.TextField(blank=True)
    image           = models.ImageField(upload_to=make_member_image_name, storage=OverwriteStorage(), blank=True)
    status          = models.IntegerField(choices=STATUS_CHOICES)
    #activation_key  = models.CharField(max_length=255, blank=True)

    def __unicode__(self):
        return unicode(self.user.get_full_name())

    @models.permalink
    def get_absolute_url(self):
        return ('cms:profile_url', (self.pk,), {})

    def generate_hashed_email(self):
        return hashlib.md5(self.user.email).hexdigest()

    def get_coordinated_projects(self):
        return Project.objects.filter(pk__in=ProjectMember.objects.filter(member__pk=self.pk, is_coordinator=True).values_list('project__pk', flat=True))

    @staticmethod
    def get_possible_project_members():
	    # need to allow member one year old as well since activity status is predicated on project membership
	    # but you can't create a project for a new year without a project coordinator
        # hence, the one year offset avoids the chicken-and-the-egg problem
        #return Member.objects.filter(Q(status=Member.STATUS_ACTIVE) | Q(pk__in=ProjectMember.objects.filter(project__year__gte= \
        #    settings.CURRENT_YEAR-1).distinct().values_list('member'))).order_by('user__first_name', 'user__last_name')
        return Member.objects.exclude(status=Member.STATUS_ARCHIVED).order_by('user__first_name', 'user__last_name')

    class Meta:
        verbose_name        = 'member'
        verbose_name_plural = 'members'
        ordering            = ['user__first_name'] class News(models.Model):
    title           = models.CharField(max_length=255)
    description     = models.TextField(max_length=200)
    content         = models.TextField()
    date            = models.DateTimeField()
    slug            = models.SlugField(max_length=100)

    def __unicode__(self):
        return unicode(self.title)

    class Meta:
        verbose_name        = 'news'
        verbose_name_plural = 'news' class Page(models.Model):
    title           = models.CharField(max_length=255)
    content         = models.TextField()
    slug            = models.SlugField(max_length=100, unique=True)
    weight          = models.IntegerField(default=0)
    pub_front_page  = models.BooleanField(default=False, verbose_name='Publish on homepage')
    pub_menu        = models.BooleanField(default=False, verbose_name='Publish on top menu')

    def __unicode__(self):
        return unicode(self.title)

    @models.permalink
    def get_absolute_url(self):
        return ('cms:page_url', (), {'slug': self.slug}) class Project(models.Model):
    STATUS_EMPTY    = 0 # this project is pending "creation" by coordinator
    STATUS_ACTIVE   = 1 # this project has been created and is editable by coordinators
    STATUS_ARCHIVED = 2 # this project has been archived and is frozen

    STATUS_CHOICES  = (
        (STATUS_EMPTY, u'Empty'),
        (STATUS_ACTIVE, u'Active'),
        (STATUS_ARCHIVED, u'Archived')
    )

    CATEGORY_OTHER          = 0
    CATEGORY_OUTREACH       = 1
    CATEGORY_RESEARCH       = 2
    CATEGORY_SERVICE        = 3
    CATEGORY_INTERNSHIP     = 4
    CATEGORY_ORGANIZATIONAL = 5

    CATEGORY_CHOICES = (
        (CATEGORY_OUTREACH, u'Outreach'),
        (CATEGORY_RESEARCH, u'Research'),
        (CATEGORY_SERVICE, u'Service'),
        (CATEGORY_INTERNSHIP, u'Internship'),
        (CATEGORY_ORGANIZATIONAL, u'Organizational'),
        (CATEGORY_OTHER, u'Other'),
    )

    title           = models.CharField(max_length=255)
    description     = models.TextField()
    image           = models.ImageField(upload_to=make_project_image_name, storage=OverwriteStorage(), blank=True)
    #active          = models.BooleanField(default=False)
    status          = models.IntegerField(choices=STATUS_CHOICES)
    category        = models.IntegerField(choices=CATEGORY_CHOICES, default=CATEGORY_OTHER)
    year            = models.IntegerField()
    members         = models.ManyToManyField(Member, through='ProjectMember')
    parent          = models.ForeignKey('self', blank=True, null=True, on_delete=models.SET_NULL)

    def __unicode__(self):
        return unicode('%s %d' % (self.title, self.year))

    @models.permalink
    def get_absolute_url(self):
        return ('cms:projects_year_pk_url', (), {'year': self.year, 'pk': self.pk})

    def is_member_coordinator(self, member):
        return ProjectMember.objects.filter(member__pk=member.pk, project__pk=self.pk, is_coordinator=True).count() != 0

    class Meta:
        verbose_name        = 'project'
        verbose_name_plural = 'projects' class ProjectMember(models.Model):
    project         = models.ForeignKey(Project)
    member          = models.ForeignKey(Member, blank=True, null=True)
    role            = models.CharField(max_length=255, blank=True)
    volunteer_name  = models.CharField(max_length=255, blank=True)
    is_coordinator  = models.BooleanField()

    class Meta:
        ordering    = [ '-is_coordinator', 'member__user__first_name', 'volunteer_name' ]

    def is_volunteer(self):
        return self.member is None

    def get_full_name(self):
        if self.is_volunteer:
            return unicode(self.volunteer_name)
        else:
            return self.member.user.get_full_name()

    def __unicode__(self):
        if self.role:
            return unicode('%s (%s)' % (self.get_full_name(), self.role))
        else:
            return self.get_full_name() class BlogPost(models.Model):
    author          = models.ForeignKey(Member)
    title           = models.CharField(max_length=255)
    date            = models.DateTimeField(auto_now_add=True)
    edit_date       = models.DateTimeField(auto_now=True)
    post            = models.TextField(help_text='HTML is allowed')
    tags            = models.ManyToManyField(Tag, blank=True, related_name='blogposts')

    objects         = BlogPostManager()

    @models.permalink
    def get_absolute_url(self):
        return ('cms:blog_post_url', (), {'pk': self.author.pk, 'blog_pk': self.pk})

    def __unicode__(self):
        return unicode('%s by %s' % (self.title, self.author))

    class Meta:
        verbose_name        = 'blog post'
        verbose_name_plural = 'blog posts'
        ordering            = ['-date'] class Tag(models.Model):
    name            = models.CharField(max_length=255)

    def __unicode__(self):
        return unicode(self.name) class Sponsor(models.Model):
    name            = models.CharField(max_length=255)
    image           = models.ImageField(upload_to=make_sponsor_image_name, storage=OverwriteStorage(), blank=True)

    def __unicode__(self):
        return unicode(self.name)

    @models.permalink
    def get_absolute_url(self):
        return ('cms:sponsors_url')

    def save(self):
        img_tmp = self.image
        self.image = None
        super(Sponsor, self).save()
        self.image = img_tmp
        return super(Sponsor, self).save()

    class Meta:
        verbose_name        = 'sponsor'
        verbose_name_plural = 'sponsors' class MemberForm(ModelForm):
    class Meta:
        model = Member
        exclude = ('user', 'status') class ProjectForm(ModelForm):
    members = forms.ModelMultipleChoiceField(queryset=Member.get_possible_project_members())

    class Meta:
        model = Project
        exclude = ('status','year')

    # special handling of ManyToMany members field
    # ref: http://stackoverflow.com/questions/387686/what-are-the-steps-to-make-a-modelform-work-with-a-manytomany-relationship-with
    def save(self, commit=True):
        project = super(ModelForm, self).save(commit=False)
        # save the regular fields
        project.save()

        # create ProjectMember instances for members listed in form that aren't added to Project yet
        for member in self.cleaned_data.get('members', []):
            try:
                project.members.get(pk=member.pk)
            except Member.DoesNotExist:
                project_member = ProjectMember(project=project, member=member, is_coordinator=False)
                project_member.save()
                #print 'add', project_member.member.user.get_full_name()

        # delete ProjectMember instances for members listed in Project but not in form
        for member in project.members.all():
            if not member in self.cleaned_data.get('members', []):
                project.projectmember_set.get(member__pk=member.pk).delete()
                #print 'delete', member.user.get_full_name()

        return project class BlogForm(ModelForm):
    class Meta:
        model = BlogPost
        exclude = ('author',) class MemberAdminForm(ModelForm):
    first_name = forms.CharField(max_length=50, required=True)
    last_name = forms.CharField(max_length=50, required=True)
    unity_id = forms.CharField(max_length=8, required=True, label='Unity ID')
    email = forms.EmailField(required=True)

    class Meta:
        model = Member
        fields = ('unity_id', 'first_name', 'last_name', 'email', 'group', 'classification',) class ProjectAdminForm(ModelForm):
    members = Member.get_possible_project_members()

    coordinators = forms.ModelMultipleChoiceField(queryset=members)

    class Meta:
        model = Project
        fields = ('title', 'coordinators', 'parent') class PageForm(ModelForm):
    class Meta:
        model = Page class SponsorForm(ModelForm):
    class Meta:
        model = Sponsor def create_profile_handler(sender, **kwargs):
def assign_coordinators_handler(sender, **kwargs): def is_user_slc_leader(user):
def can_user_create_project(user):
def can_user_edit_project(user, project):
def can_user_delete_project(user, project):
def can_user_demote_project_coordinators(user, project):
def can_user_create_member(user):
def can_user_edit_member(user, member):
def can_user_delete_member(user, member):
def can_user_archive_member(user, member):
def can_user_reactivate_member(user, member):
def can_user_post_as_member(user, member):
def can_user_edit_blogpost(user, blogpost):
def can_user_create_page(user):
def can_user_edit_page(user, page):
def can_user_delete_page(user, page):
def can_user_delete_sponsor(user):
def can_user_create_sponsor(user):
from django.core.urlresolvers import reverse
from django.conf import settings
from django.utils import simplejson
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, HttpResponseBadRequest
from django.contrib.auth.models import User
from django.views.generic import View, ListView, DetailView, CreateView, UpdateView, TemplateView, DeleteView
from django.shortcuts import get_object_or_404
from django.db.models import Q
from cms.models import Member, News, Page, Project, ProjectMember, BlogPost, Tag, Sponsor
from cms.forms import MemberForm, ProjectForm, BlogForm, MemberAdminForm, ProjectAdminForm, PageForm, SponsorForm
from cms import signals
from cms import permissions
import re
import time

    def get_success_url(self):
        return reverse('cms:members_url')


class MembersView(ListView):
    model = Member
    template_name = 'members/members.html'
    context_object_name = 'members'

    def get_context_data(self, **kwargs):
        context = super(MembersView, self).get_context_data(**kwargs)

        # NOTE: "year" is a field lookup type so must use "year__exact" instead
        project_members = ProjectMember.objects.filter(project__year__exact=self.kwargs.get('year', settings.CURRENT_YEAR))
        member_query = Q(pk__in=project_members.values_list('member'))
        context['members'] = Member.objects.filter(member_query).order_by('user__first_name', 'user__last_name').distinct()

        if permissions.is_user_slc_leader(self.request.user):
            context['archived_members'] = Member.objects.filter(status=Member.STATUS_ARCHIVED).order_by('user__first_name', 'user__last_name')

        context['project_member_groups'] = [{'group': x[1], 'members': context['members'].filter(group=x[0])} for x in Member.GROUP_CHOICES]
        context['project_member_groups'] += [{'group': 'Volunteer', 'project_members': project_members.filter(member=None).order_by('volunteer_name')}]

        context['current_year'] = settings.CURRENT_YEAR
        context['year'] = self.kwargs.get('year', settings.CURRENT_YEAR)
        next_year = int(context['year']) + 1
        context['year2'] = next_year
        prev_year = int(context['year']) - 1

        if Project.objects.filter(year=next_year).count() > 0:
            context['next_year'] = next_year
            context['next_year2'] = next_year + 1
        if Project.objects.filter(year=prev_year).count() > 0:
            context['prev_year'] = prev_year
            context['prev_year2'] = prev_year + 1

        return context

    def render_to_response(self, context):
        # check for "year" in kwargs to avoid redirect loop when current year has no data
        if len(context['members']) == 0 and 'year' in self.kwargs:
            return HttpResponseRedirect(reverse('cms:members_url'))
        else:
            return super(MembersView, self).render_to_response(context)


class DeleteMemberView(DeleteView):
    model = Member
    template_name = 'members/delete.html'

    def dispatch(self, request, *args, **kwargs):
        member = get_object_or_404(Member, pk=kwargs.get('pk', None))
        if permissions.can_user_delete_member(request.user, member):
            return super(DeleteMemberView, self).dispatch(request, *args, **kwargs)
        else:
            return HttpResponseForbidden('You do not have permission to delete members.')

    def delete(self, request, *args, **kwargs):
        self.object = self.get_object()
        self.object.user.delete()
        return HttpResponseRedirect(self.get_success_url())

    def get_success_url(self):
        return reverse('cms:members_url')


class ArchiveMemberView(DeleteView):
    model = Member
    template_name = 'members/archive.html'

    def dispatch(self, request, *args, **kwargs):
        member = get_object_or_404(Member, pk=kwargs.get('pk', None))
        if permissions.can_user_archive_member(request.user, member):
            return super(ArchiveMemberView, self).dispatch(request, *args, **kwargs)
        else:
            return HttpResponseForbidden('You do not have permission to archive members.')

    def delete(self, request, *args, **kwargs):
        self.object = self.get_object()
        self.object.status = Member.STATUS_ARCHIVED
        self.object.save()
        return HttpResponseRedirect(self.get_success_url())

    def get_success_url(self):
        return reverse('cms:members_url')


class ReactivateMemberView(UpdateView):
    model = Member
    template_name = 'members/reactivate.html'

    def dispatch(self, request, *args, **kwargs):
        member = get_object_or_404(Member, pk=kwargs.get('pk', None))
        if permissions.can_user_reactivate_member(request.user, member):
            return super(ReactivateMemberView, self).dispatch(request, *args, **kwargs)
        else:
            return HttpResponseForbidden('You do not have permission to reactivate members.')

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        self.object.status = Member.STATUS_ACTIVE
        self.object.save()
        return HttpResponseRedirect(self.get_success_url())

    def get_success_url(self):
        return reverse('cms:members_url')


class NewsView(ListView):
    model = News
    template_name = 'news/news.html'
    context_object_name = 'news_list'

class NewsDetailView(DetailView):
    model = News
    template_name = 'news/article.html'
    context_object_name = 'news'

class PageDetailView(DetailView):

    model = Page