from functools import partial import itertools import urlparse import json import collections from flask import current_app from flask.ext.wtf import Form from wtforms import Form as InsecureForm from wtforms import (TextField, DateField, DecimalField, IntegerField, SelectField, SelectMultipleField, FieldList, FormField) from wtforms.widgets import (TextInput, ListWidget, html_params, HTMLString, CheckboxInput, Select, TextArea) from wtforms.validators import (DataRequired, Optional, URL, Email, Length, NumberRange, ValidationError, StopValidation) from flask.ext.babel import lazy_gettext as _, gettext from babel.support import LazyProxy from ispformat.validator import validate_geojson from .constants import STEPS from .models import ISP from .utils import check_geojson_spatialite, filesize_fmt class InputListWidget(ListWidget): def __call__(self, field, **kwargs): kwargs.setdefault('id', field.id) html = ['<%s %s>' % (self.html_tag, html_params(**kwargs))] for subfield in field: html.append('
irc://irc.isp.net/#isp
or ' +
'xmpp:isp@chat.isp.net?join
')])
covered_areas = FieldList(MyFormField(CoveredArea, _('Covered Areas'), widget=partial(InputListWidget(), class_='formfield')),
min_entries=1, widget=InputListWidget(),
description=[None, _(u'Descriptive name of the covered areas and technologies deployed')])
latitude = DecimalField(_(u'latitude'), validators=[Optional(), NumberRange(min=-90, max=90)],
description=[None, _(u'Coordinates of your registered office or usual meeting location. '
'Required in order to appear on the map.')])
longitude = DecimalField(_(u'longitude'), validators=[Optional(), NumberRange(min=-180, max=180)])
step = SelectField(_(u'progress step'), choices=STEP_CHOICES, coerce=int)
member_count = IntegerField(_(u'members'), validators=[Optional(), NumberRange(min=0)],
description=[None, _('Number of members')])
subscriber_count = IntegerField(_(u'subscribers'), validators=[Optional(), NumberRange(min=0)],
description=[None, _('Number of subscribers to an internet access')])
tech_email = TextField(_('Email'), validators=[Email(), DataRequired()], description=[None,
_('Technical contact, in case of problems with your submission')])
def validate(self, *args, **kwargs):
r = super(ProjectForm, self).validate(*args, **kwargs)
if (self.latitude.data is None) != (self.longitude.data is None):
self._fields['longitude'].errors += [_(u'You must fill both fields')]
r = False
return r
def validate_covered_areas(self, field):
if len(filter(lambda e: e['name'], field.data)) == 0:
# not printed, whatever..
raise ValidationError(_(u'You must specify at least one area'))
geojson_size = sum([len(ca.area.raw_data[0]) for ca in self.covered_areas if ca.area.raw_data])
max_size = current_app.config['ISP_FORM_GEOJSON_MAX_SIZE_TOTAL']
if geojson_size > max_size:
# TODO: XXX This is not printed !
raise ValidationError(gettext(u'The size of all GeoJSON data combined must not exceed %(max_size)s',
max_size=filesize_fmt(max_size)))
def to_json(self, json=None):
if json is None:
json = {}
json['name'] = self.name.data
def optstr(k, v):
if k in json or v:
json[k] = v
def optlist(k, v):
if k in json or len(v):
json[k] = v
def transform_covered_areas(cas):
for ca in cas:
if not ca['name']:
continue
if 'area' in ca and ca['area'] is None:
del ca['area']
yield ca
optstr('shortname', self.shortname.data)
optstr('description', self.description.data)
optstr('logoURL', self.logo_url.data)
optstr('website', self.website.data)
optstr('otherWebsites', dict(((w['name'], w['url']) for w in self.other_websites.data if w['name'])))
optstr('email', self.contact_email.data)
optstr('mainMailingList', self.main_ml.data)
optstr('creationDate', self.creation_date.data.isoformat() if self.creation_date.data else None)
optstr('progressStatus', self.step.data)
optstr('memberCount', self.member_count.data)
optstr('subscriberCount', self.subscriber_count.data)
optlist('chatrooms', filter(bool, self.chatrooms.data)) # remove empty strings
optstr('coordinates', {'latitude': self.latitude.data, 'longitude': self.longitude.data}
if self.latitude.data else {})
optlist('coveredAreas', list(transform_covered_areas(self.covered_areas.data)))
return json
@classmethod
def edit_json(cls, isp):
json = isp.json
obj = type('abject', (object,), {})()
def set_attr(attr, itemk=None, d=json):
if itemk is None:
itemk = attr
if itemk in d:
setattr(obj, attr, d[itemk])
set_attr('name')
set_attr('shortname')
set_attr('description')
set_attr('logo_url', 'logoURL')
set_attr('website')
set_attr('contact_email', 'email')
set_attr('main_ml', 'mainMailingList')
set_attr('creation_date', 'creationDate')
if hasattr(obj, 'creation_date'):
obj.creation_date = ISP.str2date(obj.creation_date)
set_attr('step', 'progressStatus')
set_attr('member_count', 'memberCount')
set_attr('subscriber_count', 'subscriberCount')
set_attr('chatrooms', 'chatrooms')
if 'coordinates' in json:
set_attr('latitude', d=json['coordinates'])
set_attr('longitude', d=json['coordinates'])
if 'otherWebsites' in json:
setattr(obj, 'other_websites', [{'name': n, 'url': w} for n, w in json['otherWebsites'].iteritems()])
set_attr('covered_areas', 'coveredAreas')
obj.tech_email = isp.tech_email
return cls(obj=obj)
class URLField(TextField):
def _value(self):
if isinstance(self.data, basestring):
return self.data
elif self.data is None:
return ''
else:
return urlparse.urlunsplit(self.data)
def process_formdata(self, valuelist):
if valuelist:
try:
self.data = urlparse.urlsplit(valuelist[0])
except:
self.data = None
raise ValidationError(_(u'Invalid URL'))
def is_url_unique(url):
if isinstance(url, basestring):
url = urlparse.urlsplit(url)
t = list(url)
t[2] = ''
u1 = urlparse.urlunsplit(t)
t[0] = 'http' if t[0] == 'https' else 'https'
u2 = urlparse.urlunsplit(t)
if ISP.query.filter(ISP.json_url.startswith(u1) | ISP.json_url.startswith(u2)).count() > 0:
return False
return True
class ProjectJSONForm(Form):
json_url = URLField(_(u'base url'), description=[_(u'E.g. https://isp.com/'),
_(u'A ressource implementing our JSON-Schema specification ' +
'must exist at path /isp.json')])
tech_email = TextField(_(u'Email'), validators=[Email()], description=[None,
_(u'Technical contact, in case of problems')])
def validate_json_url(self, field):
if not field.data.netloc:
raise ValidationError(_(u'Invalid URL'))
if field.data.scheme not in ('http', 'https'):
raise ValidationError(_(u'Invalid URL (must be HTTP(S))'))
if not field.object_data and not is_url_unique(field.data):
raise ValidationError(_(u'This URL is already in our database'))
class RequestEditToken(Form):
tech_email = TextField(_(u'Tech Email'), validators=[Email()], description=[None,
_(u'The Technical contact you provided while registering')])