views.py 15 KB
Newer Older
stef's avatar
stef committed
1
from forms import AddViolation, SearchViolation
2 3
from django.views.generic import ListView, FormView, DetailView
from django.views.generic.list import MultipleObjectMixin
stef's avatar
stef committed
4
from django.http import HttpResponse, HttpResponseRedirect, Http404
5
from django.shortcuts import render_to_response, get_object_or_404, redirect
6
from django.template import RequestContext, loader, Context
stef's avatar
stef committed
7
from django.core.files import File
8
from django.core.servers.basehttp import FileWrapper
Okhin's avatar
Okhin committed
9
from django.core import serializers
10
from django.conf import settings
stef's avatar
stef committed
11
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
12
from django.core.exceptions import ObjectDoesNotExist
13
from django.core.mail import send_mail
14
from django.contrib import messages
15
from django.contrib.auth.models import User
stef's avatar
stef committed
16
from django.utils.translation import ugettext_lazy as _
stef's avatar
stef committed
17
from django.db.models import Count
18
from haystack.generic_views import SearchView
Okhin's avatar
Okhin committed
19
from haystack.query import SearchQuerySet
20
from models import Violation, Attachment, Comment, Confirmation, COUNTRIES, STATUS, Operator, FeaturedCase
stef's avatar
stef committed
21 22
from tempfile import mkstemp
from datetime import datetime
23
import hashlib, os, re, json
24 25
from random import randint
from email.mime.text import MIMEText
stef's avatar
stef committed
26
from email.header import Header
27 28
from urlparse import urljoin
from BeautifulSoup import BeautifulSoup, Comment as BComment
stef's avatar
stef committed
29
from operator import itemgetter
30
from itertools import groupby
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55

def sanitizeHtml(value, base_url=None):
    rjs = r'[\s]*(&#x.{1,7})?'.join(list('javascript:'))
    rvb = r'[\s]*(&#x.{1,7})?'.join(list('vbscript:'))
    re_scripts = re.compile('(%s)|(%s)' % (rjs, rvb), re.IGNORECASE)
    validTags = 'p i strong b u a h1 h2 h3 pre br img'.split()
    validAttrs = 'href src width height'.split()
    urlAttrs = 'href src'.split() # Attributes which should have a URL
    soup = BeautifulSoup(value)
    for comment in soup.findAll(text=lambda text: isinstance(text, BComment)):
        # Get rid of comments
        comment.extract()
    for tag in soup.findAll(True):
        if tag.name not in validTags:
            tag.hidden = True
        attrs = tag.attrs
        tag.attrs = []
        for attr, val in attrs:
            if attr in validAttrs:
                val = re_scripts.sub('', val) # Remove scripts (vbs & js)
                if attr in urlAttrs:
                    val = urljoin(base_url, val) # Calculate the absolute url
                tag.attrs.append((attr, val))

    return soup.renderContents().decode('utf8')
stef's avatar
stef committed
56

57
def activate(request):
58 59 60
    try:
        v=Violation.objects.get(activationid=request.GET.get('key','asdf'))
    except:
61 62
        messages.add_message(request, messages.INFO, unicode(_('Thank you, this key has been already activated')))
        return HttpResponseRedirect('/') # Redirect after POST
63 64 65
    if v:
        actid = hashlib.sha1(''.join([chr(randint(32, 122)) for x in range(12)])).hexdigest()
        to=[x.email for x in User.objects.filter(groups__name='moderator')]
stef's avatar
stef committed
66
        details='\n'.join(["%s: %s" % (k.capitalize(), val) for k,val in v.__dict__.items() if not k.startswith('_') and val])
67 68 69 70 71 72 73 74 75

        msg = {'from': 'nnmon@respectmynet.eu',
               'to': to,
               'subject': 'NNMon submission approval'.encode("Utf-8"),
               'body': "A new report was submitted. To approve click here: %s/moderate/?key=%s\n\nDetails follow:\n%s\n%s" %
                       (settings.ROOT_URL or 'http://localhost:8001/', actid, details, v.comment_set.get().comment)
               }
        send_mail(msg['subject'], msg['body'], msg['from'], msg['to'], fail_silently=False)

76 77
        v.activationid=actid
        v.save()
Benjamin Sonntag's avatar
Benjamin Sonntag committed
78
        messages.add_message(request, messages.INFO, _('Thank you for verifying your submission. It will be listed shortly, after we\'ve checked that the report is valid.').encode("Utf-8"))
79 80
    return HttpResponseRedirect('/') # Redirect after POST

81
def moderate(request):
82 83 84
    try:
        v=Violation.objects.get(activationid=request.GET.get('key','asdf'))
    except:
85 86
        messages.add_message(request, messages.INFO, unicode(_('Thank you, this key has been already activated')))
        return HttpResponseRedirect('/') # Redirect after POST
87 88 89 90
    if not v:
        messages.add_message(request, messages.INFO, _('No such key'))
        return HttpResponseRedirect('/') # Redirect after POST
    if request.GET.get('action','')=='approve':
stef's avatar
stef committed
91
        messages.add_message(request, messages.INFO, _('Thank you for approving the <a href="%s">submission</a>.' % v.get_absolute_url()))
stef's avatar
stef committed
92

93 94 95
        msg = {'from': 'nnmon@respectmynet.eu',
               'to': [v.comment_set.get().submitter_email],
               'subject': _('NNMon submission approved').encode("Utf-8"),
stef's avatar
stef committed
96
               'body': _("Your report has been approved.\nTo see it, please visit: %s/%s") %
stef's avatar
stef committed
97
                      (settings.ROOT_URL or 'http://localhost:8001/', v.get_absolute_url())
98 99 100
               }
        send_mail(msg['subject'], msg['body'], msg['from'], msg['to'], fail_silently=False)

101
        if settings.TWITTER_API:
stef's avatar
stef committed
102
            try:
stef's avatar
stef committed
103
                settings.TWITTER_API.PostUpdate("New #NetNeutrality violation reported for %s (%s) %s %s/%s" % (v.operator, v.country, v.contract, settings.ROOT_URL or 'http://localhost:8001/', v.id))
stef's avatar
stef committed
104 105
            except:
                pass
106 107
        v.activationid=''
        v.save()
108
        return redirect(v) # Redirect after POST to violation url
109 110 111 112 113 114
    if request.GET.get('action','')=='delete':
        v.delete()
        messages.add_message(request, messages.INFO, _('Thank you for deleting the submission.'))
        return HttpResponseRedirect('/') # Redirect after POST
    return render_to_response('view.html', { 'v': v, 'key': request.GET.get('key') },context_instance=RequestContext(request))

stef's avatar
stef committed
115 116 117
def confirm(request, id, name=None):
    if name:
        if Confirmation.objects.filter(email=name, violation=id).count()==0:
stef's avatar
stef committed
118 119
            msg=_("Thank you for confirming a case. To finalize your confirmation please validate using your confirmation key.\nYour confirmation key is %s/%s%s")
            actid=sendverifymail('confirm/',name, msg)
120 121 122
            try:
                c=Confirmation(key=actid, email=name, violation=Violation.objects.get(pk=id))
            except:
stef's avatar
stef committed
123
                return HttpResponse(unicode(_('Thank you, this has been already confirmed')))
stef's avatar
stef committed
124
            c.save()
stef's avatar
stef committed
125
        return HttpResponse(unicode(_('Thank you for your confirmation')))
stef's avatar
stef committed
126 127 128
    try:
        c = get_object_or_404(Confirmation, key=id)
    except:
129 130
        messages.add_message(request, messages.INFO, unicode(_("Thank you, this has been already confirmed")))
        return HttpResponseRedirect('/') # Redirect after POST
stef's avatar
stef committed
131 132
    c.key=''
    c.save()
stef's avatar
stef committed
133
    messages.add_message(request, messages.INFO, unicode(_('Thank you for verifying your confirmation')))
134
    return HttpResponseRedirect('/') # Redirect after POST
stef's avatar
stef committed
135

stef's avatar
stef committed
136
def sendverifymail(service,to,body):
stef's avatar
stef committed
137
    actid = hashlib.sha1(''.join([chr(randint(32, 122)) for x in range(12)])).hexdigest()
138 139 140
    msg = {'from': 'nnmon@respectmynet.eu',
           'to': [to.encode("Utf-8")],
           'subject': _('NNMon submission verification').encode("Utf-8"),
stef's avatar
stef committed
141
           'body': body % (settings.ROOT_URL or 'http://localhost:8001/', service, actid),
142 143 144
           }
    send_mail(msg['subject'], msg['body'], msg['from'], msg['to'], fail_silently=False)

stef's avatar
stef committed
145 146
    return actid

Okhin's avatar
Okhin committed
147 148 149
class AddForm(FormView):
    model = Violation
    template_name = 'index.html'
150 151 152 153 154 155 156 157 158
    form_class = AddViolation
    success_url = '/'
    context_object_name = 'violations'

    def form_valid(self, form):
        msg=_("Thank you for submitting a new report. To finalize your submission please confirm using your validation key.\nYour verification key is %s/%s%s\nPlease note that reports are moderated, it might take some time before your report appears online. Thank you for your patience.")
        actid=sendverifymail('activate?key=',form.cleaned_data['email'], msg)
        operator, created = Operator.objects.get_or_create(name=form.cleaned_data['operator'])
        v=Violation(
stef's avatar
stef committed
159
                country = form.cleaned_data['country'],
160
                operator_ref = operator,
stef's avatar
stef committed
161 162
                contract = form.cleaned_data['contract'],
                resource = form.cleaned_data['resource'],
stef's avatar
stef committed
163
                resource_name = form.cleaned_data['resource_name'],
stef's avatar
stef committed
164 165 166 167
                type = form.cleaned_data['type'],
                media = form.cleaned_data['media'],
                temporary = form.cleaned_data['temporary'],
                contractual = form.cleaned_data['contractual'],
168
                contract_excerpt = sanitizeHtml(form.cleaned_data['contract_excerpt']),
169 170
                loophole = form.cleaned_data['loophole'],
                activationid = actid
stef's avatar
stef committed
171
                )
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
        v.save()
        #c=Confirmation(key='', email=form.cleaned_data['email'], violation=v)
        #c.save()
        c = Comment(
            comment=form.cleaned_data['comment'],
            submitter_email=form.cleaned_data['email'],
            submitter_name=form.cleaned_data['nick'],
            consent=form.cleaned_data['consent'],
            timestamp=datetime.now(),
            violation=v,
            )
        c.save()
        for f in request.FILES.getlist('attachments[]'):
            a=Attachment(comment=c, name=f.name, type=f.content_type)
            m = hashlib.sha256()
            for chunk in f.chunks():
                m.update(chunk)
            sname=m.hexdigest()
            a.storage.save(sname,f)
            a.save()
        messages.add_message(request, messages.INFO, _('Thank you for submitting this report, you will receive a verification email immediately, if not check your spam folder.'))
        return super(AddForm, self).form_valid(form)
stef's avatar
stef committed
194

195 196
    def get_queryset(self):
        return Violation.objects.filter(activationid='',featuredcase__isnull=False).order_by('id').reverse()[:3]
stef's avatar
stef committed
197

198 199 200 201
    def get_context_data(self, **kwargs):
        context = super(AddForm, self).get_context_data(**kwargs)
        reports = sorted([(i['total'],i['id'])
                         for i in Violation.objects.values('id').filter(activationid='').exclude(state__in=['closed', 'ooscope', 'duplicate']).annotate(total=Count('confirmation'))],
stef's avatar
stef committed
202
                    reverse=True)
203 204
        confirms = sorted([(i['total'],i['country'])
                         for i in Violation.objects.values('country').filter(activationid='').exclude(state__in=['closed', 'ooscope', 'duplicate']).annotate(total=Count('confirmation'))],
stef's avatar
stef committed
205
                    reverse=True)
206 207
        operators = sorted([(i['total'],i['operator_ref__name'])
                         for i in Violation.objects.values('operator_ref__name').filter(activationid='').exclude(state__in=['closed', 'ooscope', 'duplicate']).annotate(total=Count('confirmation'))],
stef's avatar
stef committed
208
                     reverse=True)
209
        context['stats'] = [
210
                (_('Total confirmed reports'), len([i for i,z in reports if i>0])),
211
                (_('Countries with some confirmed reports'), len([i for i,z in confirms if i>0])),
Okhin's avatar
Okhin committed
212
                (_('Operators with some confirmed reports'), len([i for i,z in operators if i>0]))]
213
        context['violations'] = [fc.case for fc in FeaturedCase.objects.all()]
214
        return context
stef's avatar
stef committed
215

216 217 218
class ViolationsList(ListView):
    template_name = 'list.html'
    context_object_name = 'violations'
219

220 221 222
    def get_queryset(self):
        queryset = Violation.objects.filter(activationid='')
        if 'operator' in self.kwargs:
Okhin's avatar
Okhin committed
223 224 225 226
            # If i Have operator I have a country
            queryset = Violation.objects.filter(activationid='',
                    country=self.kwargs['country'],
                    operator_ref__name=self.kwargs['operator'])
227
        if 'country' in self.kwargs:
Okhin's avatar
Okhin committed
228 229 230
            queryset = Violation.objects.filter(activationid='',
                    country=self.kwargs['country'])
        if 'all' not in self.request.GET:
Okhin's avatar
Okhin committed
231
            queryset = queryset.filter(activationid='').exclude(state__in=['duplicate', 'closed'])
232
        return queryset
stef's avatar
stef committed
233

234 235 236 237 238
    def get_context_data(self, **kwargs):
        context = super(ViolationsList, self).get_context_data(**kwargs)

        if 'country' in self.kwargs:
            context['country'] = self.kwargs['country']
Okhin's avatar
Okhin committed
239 240 241 242 243 244 245
        else:
            countries = sorted([(i['total'], i['country'])
                for i in Violation.objects.values('country').filter(activationid='').exclude(state__in=['duplicate', 'closed']).annotate(total=Count('country'))],
                reverse=True)
            countryweights=json.dumps([{'iso2': y, 'w': x} for x, y in countries])
            context['countries'] = countries
            context['countryweights'] = countryweights
246 247 248 249 250 251 252 253 254 255 256 257 258
        return context

class ViolationView(DetailView):
    model = Violation
    template_name = 'view.html'
    pk_url_kwarg = 'id'
    context_object_name = 'v'

    def get_object(self):
        object = super(ViolationView, self).get_object()
        if object.activationid:
            raise Http404
        return object
stef's avatar
stef committed
259

Okhin's avatar
Okhin committed
260
class LookupView(SearchView):
261 262
    form_class = SearchViolation

263
class ViolationSearchView(SearchView):
Okhin's avatar
Okhin committed
264 265
    form_class = SearchViolation

266 267
    def get_context_data(self, *args, **kwargs):
        context = super(ViolationSearchView, self).get_context_data(*args, **kwargs)
Okhin's avatar
Okhin committed
268
        countries = sorted([(k, len(list(g)),) for k, g in groupby(sorted([i['country'] for i in self.queryset.values('country')]))])
Okhin's avatar
Okhin committed
269
        countryweights=json.dumps([{'iso2': x, 'w': y} for x, y in countries])
Okhin's avatar
Okhin committed
270 271
        operators = sorted([(k, len(list(g)),) for k, g in groupby(sorted([i['operator_name'] for i in self.queryset.values('operator_name')]))])
        context['operators'] = operators
272 273 274
        context['countries'] = countries
        context['countryweights'] = countryweights
        context['stats'] = [
Okhin's avatar
Okhin committed
275 276 277
                (_('Number of reports in this search'), len(self.queryset.all())),
                (_('Countries with some confirmed reports'), len([c for c,v in countries if v > 0])),
                (_('Operators with some confirmed reports'), len([c for c,v in operators if v > 0])),
278 279 280
                ]
        return context

281 282 283 284
def get_attach(request,id):
    f = get_object_or_404(Attachment, pk=id)
    wrapper = FileWrapper(f.storage)
    response=HttpResponse(wrapper, mimetype=f.type)
285
    response['Content-Disposition'] = 'attachment; filename="%s"' % f.name
286 287 288
    response['Content-Length'] = f.storage.size
    return response

289 290 291 292 293 294 295 296 297 298 299 300 301
def ascsv(request):
    response = HttpResponse(mimetype='text/csv')
    response['Content-Disposition'] = 'attachment; filename=respectmynet.csv'

    res=[]
    for v in Violation.objects.filter(activationid=''):
        res.append((v.state, v.country, v.operator, v.contract, v.resource, v.resource_name, v.type, v.media, v.temporary, v.contractual, v.contract_excerpt, v.loophole, v.editorial,v.comment_set.get().comment))
    t = loader.get_template('csv.tmpl')
    c = Context({
        'data': res,
    })
    response.write(t.render(c))
    return response
302 303 304 305 306 307 308 309 310 311

from sheet import save_ods
def asods(request):
    response = HttpResponse(mimetype='application/vnd.oasis.opendocument.spreadsheet')
    response['Content-Disposition'] = 'attachment; filename=respectmynet-ec_berec_tm_questionnaire.ods'
    save_ods()
    f=open('/tmp/ec_berec_tm_questionnaire.ods','r')
    response.write(f.read())
    f.close()
    return response