views.py 16.3 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
Okhin's avatar
Okhin committed
5
from django.shortcuts import render_to_response, get_object_or_404, redirect, render
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
class AddForm(FormView):
    template_name = 'index.html'
149
150
151
152
    form_class = AddViolation
    success_url = '/'

    def form_valid(self, form):
153
154
155
        #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)
        actid = hashlib.sha1(''.join([chr(randint(32, 122)) for x in range(12)])).hexdigest()
156
157
        operator, created = Operator.objects.get_or_create(name=form.cleaned_data['operator'])
        v=Violation(
stef's avatar
stef committed
158
                country = form.cleaned_data['country'],
159
                operator_ref = operator,
stef's avatar
stef committed
160
161
                contract = form.cleaned_data['contract'],
                resource = form.cleaned_data['resource'],
stef's avatar
stef committed
162
                resource_name = form.cleaned_data['resource_name'],
stef's avatar
stef committed
163
164
165
166
                type = form.cleaned_data['type'],
                media = form.cleaned_data['media'],
                temporary = form.cleaned_data['temporary'],
                contractual = form.cleaned_data['contractual'],
167
                contract_excerpt = sanitizeHtml(form.cleaned_data['contract_excerpt']),
168
                loophole = form.cleaned_data['loophole'],
Okhin's avatar
Okhin committed
169
170
                activationid = actid,
                old = False
stef's avatar
stef committed
171
                )
172
        v.save()
173
174
        conf=Confirmation(key=actid, email=form.cleaned_data['email'], violation=v)
        conf.save()
175
176
177
178
179
180
181
182
183
        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()
Okhin's avatar
Okhin committed
184
        for f in self.request.FILES.getlist('attachments[]'):
185
186
187
188
189
190
191
            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()
Okhin's avatar
Okhin committed
192
        messages.add_message(self.request, messages.INFO, _('Thank you for submitting this report, you will receive a verification email immediately, if not check your spam folder.'))
193
        return super(AddForm, self).form_valid(form)
stef's avatar
stef committed
194

Okhin's avatar
Okhin committed
195
196
197
198
199
200
201
202
203
    def post(self, request, *args, **kwargs):
        # We needto do special work with the form view
        form = self.form_class(request.POST, request.FILES)
        if form.is_valid():
            form.save()
            return redirect(self.success_url)
        else:
            return render(request, self.template_name, {'form': form})

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

222
223
224
class ViolationsList(ListView):
    template_name = 'list.html'
    context_object_name = 'violations'
225

226
    def get_queryset(self):
227
        queryset = Violation.objects.exclude(old=True)
228
        if 'operator' in self.kwargs:
Okhin's avatar
Okhin committed
229
            # If i Have operator I have a country
230
231
            queryset = Violation.objects.filter(country=self.kwargs['country'],
                    operator_ref__name=self.kwargs['operator'])
232
        if 'country' in self.kwargs:
233
            queryset = Violation.objects.filter(country=self.kwargs['country'])
Okhin's avatar
Okhin committed
234
        if 'all' not in self.request.GET:
235
            queryset = queryset.exclude(state__in=['ooscope', 'duplicate', 'closed'])
236
        return queryset
stef's avatar
stef committed
237

238
239
240
241
242
    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
243
244
        else:
            countries = sorted([(i['total'], i['country'])
245
                for i in Violation.objects.values('country').exclude(state__in=['ooscope', 'duplicate', 'closed']).exclude(old=True).annotate(total=Count('country'))],
Okhin's avatar
Okhin committed
246
247
248
249
                reverse=True)
            countryweights=json.dumps([{'iso2': y, 'w': x} for x, y in countries])
            context['countries'] = countries
            context['countryweights'] = countryweights
250
251
252
253
254
255
256
257
258
259
260
261
262
        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
263

Okhin's avatar
Okhin committed
264
class LookupView(SearchView):
Okhin's avatar
Okhin committed
265
    searchqueryset = SearchQuerySet().exclude(old=True)
266
267
    form_class = SearchViolation

Okhin's avatar
Okhin committed
268
269
270
271
272
273
274
275
276
    def get_queryset(self):
        return super(LookupView, self).get_queryset().exclude(old=True)

    def get_context_data(self, *args, **kwargs):
        context = super(LookupView, self).get_context_data(*args, **kwargs)
        if 'object_list' in context:
            context['object_list'] = [obj for obj in context['object_list'] if not obj.object.old]
        return context

277
class ViolationSearchView(SearchView):
Okhin's avatar
Okhin committed
278
    searchqueryset = SearchQuerySet().exclude(old=True)
Okhin's avatar
Okhin committed
279
280
    form_class = SearchViolation

Okhin's avatar
Okhin committed
281
282
283
    def get_queryset(self):
        return super(ViolationSearchView, self).get_queryset().exclude(old=True)

284
285
    def get_context_data(self, *args, **kwargs):
        context = super(ViolationSearchView, self).get_context_data(*args, **kwargs)
Okhin's avatar
Okhin committed
286
        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
287
        countryweights=json.dumps([{'iso2': x, 'w': y} for x, y in countries])
Okhin's avatar
Okhin committed
288
289
        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
290
291
292
        context['countries'] = countries
        context['countryweights'] = countryweights
        context['stats'] = [
Okhin's avatar
Okhin committed
293
294
295
                (_('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])),
296
297
298
                ]
        return context

299
300
301
302
def get_attach(request,id):
    f = get_object_or_404(Attachment, pk=id)
    wrapper = FileWrapper(f.storage)
    response=HttpResponse(wrapper, mimetype=f.type)
303
    response['Content-Disposition'] = 'attachment; filename="%s"' % f.name
304
305
306
    response['Content-Length'] = f.storage.size
    return response

Okhin's avatar
Okhin committed
307
308
309
310
311
312
313
314
315
316
317
318
class ViolationCsvList(ListView):
    model = Violation
    template_name = 'csv.tmpl'
    content_type = 'text/csv'
    csv_name = 'respectmynet.csv'
    queryset = Violation.objects.all().annotate(confirmations=Count('confirmation')).order_by('-confirmations')

    def render_to_response(self, context, **kwargs):
        response = super(ListView, self).render_to_response(context, **kwargs)
        response['Content-Disposition']= 'attachment; filename={0}'.format(self.csv_name)
        return response

319
def ascsv(request):
Okhin's avatar
Okhin committed
320
    response = HttpResponse(content_type='text/csv')
321
322
323
324
325
326
327
328
329
330
331
    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
332
333
334
335
336
337
338
339
340
341

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