views.py 15.4 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
class AddForm(FormView):
    template_name = 'index.html'
149
150
151
152
153
154
155
156
    form_class = AddViolation
    success_url = '/'

    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
157
                country = form.cleaned_data['country'],
158
                operator_ref = operator,
stef's avatar
stef committed
159
160
                contract = form.cleaned_data['contract'],
                resource = form.cleaned_data['resource'],
stef's avatar
stef committed
161
                resource_name = form.cleaned_data['resource_name'],
stef's avatar
stef committed
162
163
164
165
                type = form.cleaned_data['type'],
                media = form.cleaned_data['media'],
                temporary = form.cleaned_data['temporary'],
                contractual = form.cleaned_data['contractual'],
166
                contract_excerpt = sanitizeHtml(form.cleaned_data['contract_excerpt']),
167
                loophole = form.cleaned_data['loophole'],
Okhin's avatar
Okhin committed
168
169
                activationid = actid,
                old = False
stef's avatar
stef committed
170
                )
171
172
173
174
175
176
177
178
179
180
181
182
        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()
Okhin's avatar
Okhin committed
183
        for f in self.request.FILES.getlist('attachments[]'):
184
185
186
187
188
189
190
            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
191
        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.'))
192
        return super(AddForm, self).form_valid(form)
stef's avatar
stef committed
193

194
195
196
    def get_context_data(self, **kwargs):
        context = super(AddForm, self).get_context_data(**kwargs)
        reports = sorted([(i['total'],i['id'])
197
                         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
198
                    reverse=True)
199
        confirms = sorted([(i['total'],i['country'])
200
                         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
201
                    reverse=True)
202
        operators = sorted([(i['total'],i['operator_ref__name'])
203
                         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
204
                     reverse=True)
205
        context['stats'] = [
206
                (_('Total confirmed reports'), len([i for i,z in reports if i>0])),
207
                (_('Countries with some confirmed reports'), len([i for i,z in confirms if i>0])),
Okhin's avatar
Okhin committed
208
                (_('Operators with some confirmed reports'), len([i for i,z in operators if i>0]))]
Okhin's avatar
Okhin committed
209
        context['violations'] = [fc.case for fc in FeaturedCase.objects.all() if not fc.case.old][:3]
210
        return context
stef's avatar
stef committed
211

212
213
214
class ViolationsList(ListView):
    template_name = 'list.html'
    context_object_name = 'violations'
215

216
    def get_queryset(self):
217
        queryset = Violation.objects.exclude(old=True)
218
        if 'operator' in self.kwargs:
Okhin's avatar
Okhin committed
219
            # If i Have operator I have a country
220
221
            queryset = Violation.objects.filter(country=self.kwargs['country'],
                    operator_ref__name=self.kwargs['operator'])
222
        if 'country' in self.kwargs:
223
            queryset = Violation.objects.filter(country=self.kwargs['country'])
Okhin's avatar
Okhin committed
224
        if 'all' not in self.request.GET:
225
            queryset = queryset.exclude(state__in=['ooscope', 'duplicate', 'closed'])
226
        return queryset
stef's avatar
stef committed
227

228
229
230
231
232
    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
233
234
        else:
            countries = sorted([(i['total'], i['country'])
235
                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
236
237
238
239
                reverse=True)
            countryweights=json.dumps([{'iso2': y, 'w': x} for x, y in countries])
            context['countries'] = countries
            context['countryweights'] = countryweights
240
241
242
243
244
245
246
247
248
249
250
251
252
        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
253

Okhin's avatar
Okhin committed
254
class LookupView(SearchView):
Okhin's avatar
Okhin committed
255
    searchqueryset = SearchQuerySet().exclude(old=True)
256
257
    form_class = SearchViolation

Okhin's avatar
Okhin committed
258
259
260
261
262
263
264
265
266
    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

267
class ViolationSearchView(SearchView):
Okhin's avatar
Okhin committed
268
    searchqueryset = SearchQuerySet().exclude(old=True)
Okhin's avatar
Okhin committed
269
270
    form_class = SearchViolation

Okhin's avatar
Okhin committed
271
272
273
    def get_queryset(self):
        return super(ViolationSearchView, self).get_queryset().exclude(old=True)

274
275
    def get_context_data(self, *args, **kwargs):
        context = super(ViolationSearchView, self).get_context_data(*args, **kwargs)
Okhin's avatar
Okhin committed
276
        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
277
        countryweights=json.dumps([{'iso2': x, 'w': y} for x, y in countries])
Okhin's avatar
Okhin committed
278
279
        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
280
281
282
        context['countries'] = countries
        context['countryweights'] = countryweights
        context['stats'] = [
Okhin's avatar
Okhin committed
283
284
285
                (_('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])),
286
287
288
                ]
        return context

289
290
291
292
def get_attach(request,id):
    f = get_object_or_404(Attachment, pk=id)
    wrapper = FileWrapper(f.storage)
    response=HttpResponse(wrapper, mimetype=f.type)
293
    response['Content-Disposition'] = 'attachment; filename="%s"' % f.name
294
295
296
    response['Content-Length'] = f.storage.size
    return response

297
298
299
300
301
302
303
304
305
306
307
308
309
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
310
311
312
313
314
315
316
317
318
319

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