# -*- coding: utf-8 -*- import re import io from django import forms from django import template from django.contrib.auth.models import Permission from django.utils.translation import ugettext_lazy as _ from servo.forms.base import BaseForm, BaseModelForm from servo.forms.account import ProfileForm from servo.models.common import * from servo.models.queue import * from servo.models import User, Group, Checklist class UserUploadForm(forms.Form): datafile = forms.FileField() location = forms.ModelChoiceField(queryset=Location.objects.filter(enabled=True)) queues = forms.ModelMultipleChoiceField( queryset=Queue.objects.all() ) group = forms.ModelChoiceField(queryset=Group.objects.all()) queues = forms.ModelMultipleChoiceField( queryset=Queue.objects.all() ) def save(self, **kwargs): users = [] string = u'' cd = self.cleaned_data data = cd['datafile'].read() for i in ('utf-8', 'latin-1',): try: string = data.decode(i) except: pass if not string: raise ValueError(_('Unsupported file encoding')) sio = io.StringIO(string, newline=None) for l in sio.readlines(): cols = l.strip().split("\t") if len(cols) < 2: continue # Skip empty rows user = User(username=cols[2]) user.first_name = cols[0] user.last_name = cols[1] user.email = cols[3] user.set_password(cols[4]) user.save() user.location = cd['location'] user.timezone = user.location.timezone user.groups.add(cd['group']) user.queues = cd['queues'] user.save() users.append(user) return users class GsxAccountForm(forms.ModelForm): class Meta: model = GsxAccount exclude = [] class GroupForm(forms.ModelForm): class Meta: exclude = [] model = Group user_set = forms.ModelMultipleChoiceField( required=False, label=_('Group members'), queryset=User.active.all(), widget=forms.CheckboxSelectMultiple ) permissions = forms.ModelMultipleChoiceField( required=False, label=_('Permissions'), widget=forms.CheckboxSelectMultiple, queryset=Permission.objects.filter(content_type__app_label='servo') ) def __init__(self, *args, **kwargs): super(GroupForm, self).__init__(*args, **kwargs) if self.instance.pk: user_ids = [u.pk for u in self.instance.user_set.all()] self.fields['user_set'].initial = user_ids def save(self, *args, **kwargs): group = super(GroupForm, self).save(*args, **kwargs) group.user_set.clear() for u in self.cleaned_data['user_set']: group.user_set.add(u) return group class ChecklistForm(BaseModelForm): class Meta: model = Checklist exclude = [] widgets = {'queues': forms.CheckboxSelectMultiple} class LocationForm(BaseModelForm): class Meta: model = Location exclude = [] widgets = {'gsx_accounts': forms.CheckboxSelectMultiple} def save(self, **kwargs): from django.db.utils import IntegrityError try: location = super(LocationForm, self).save(**kwargs) except IntegrityError: msg = _('A location with that name already exists') self._errors['title'] = self.error_class([msg]) raise forms.ValidationError(msg) return location class QueueForm(BaseModelForm): gsx_soldto = forms.ChoiceField(required=False, choices=()) users = forms.ModelMultipleChoiceField(queryset=User.active.all(), widget=forms.CheckboxSelectMultiple, required=False) class Meta: model = Queue exclude = ('statuses',) widgets = { 'description': forms.Textarea(attrs={'rows': 4}), 'keywords': forms.Textarea(attrs={'rows': 4}), 'locations': forms.CheckboxSelectMultiple, } def __init__(self, *args, **kwargs): super(QueueForm, self).__init__(*args, **kwargs) self.fields['gsx_soldto'].choices = GsxAccount.get_soldto_choices() if "instance" in kwargs: queue = kwargs['instance'] queryset = QueueStatus.objects.filter(queue=queue) self.fields['status_created'].queryset = queryset self.fields['status_assigned'].queryset = queryset self.fields['status_products_ordered'].queryset = queryset self.fields['status_products_received'].queryset = queryset self.fields['status_repair_completed'].queryset = queryset self.fields['status_dispatched'].queryset = queryset self.fields['status_closed'].queryset = queryset def clean_order_template(self): from servo.lib.utils import file_type tpl = self.cleaned_data.get('order_template') if tpl is None: # file was not uploaded return ftype = file_type(tpl.file.read()) if ftype != 'text/html': raise forms.ValidationError(_('Print tempates must be in HTML format')) return tpl class StatusForm(BaseModelForm): class Meta: model = Status exclude = [] widgets = { 'site': forms.HiddenInput, 'limit_green': forms.TextInput(attrs={'class': 'input-mini'}), 'limit_yellow': forms.TextInput(attrs={'class': 'input-mini'}), } class QueueStatusForm(BaseModelForm): class Meta: model = QueueStatus exclude = [] widgets = {'idx': forms.Select()} def __init__(self, *args, **kwargs): super(QueueStatusForm, self).__init__(*args, **kwargs) statuses = QueueStatus.objects.filter(queue_id=self.instance.queue_id) statuses = statuses.count() or 1 self.fields['idx'].widget.choices = [(i, i) for i in range(1, statuses+1)] class UserForm(ProfileForm): def clean_username(self): reserved = ( 'admin', 'orders', 'sales', 'devices', 'customers', 'notes', 'api', 'checkin', 'feedback', ) username = self.cleaned_data.get('username') if username in reserved: raise forms.ValidationError(_(u'"%s" cannot be used as a username') % username) return username class Meta: model = User fields = ( "first_name", "last_name", "username", "email", "is_active", "groups", "is_staff", "location", "locations", "locale", "queues", "region", "timezone", "tech_id", "gsx_userid", "customer", "gsx_poprefix", ) widgets = { 'locations': forms.CheckboxSelectMultiple, 'queues': forms.CheckboxSelectMultiple } class TemplateForm(BaseModelForm): class Meta: model = Template exclude = [] widgets = { 'title': forms.TextInput(attrs={'class': 'span12'}), 'content': forms.Textarea(attrs={'class': 'span12'}) } def clean_content(self): content = self.cleaned_data.get('content') try: template.Template(content) except template.TemplateSyntaxError as e: raise forms.ValidationError(_('Syntax error in template: %s') % e) return content class SettingsForm(BaseForm): # Servo's general System Settings form company_name = forms.CharField(label=_('Company Name')) company_logo = forms.ImageField( label=_('Company Logo'), required=False, help_text=_('Company-wide logo to use in print templates') ) terms_of_service = forms.CharField( required=False, label=_('Terms of Service'), widget=forms.Textarea(attrs={'class': 'span10'}), help_text=_('These terms will be added to your work confirmations and public check-in site.') ) autocomplete_repairs = forms.BooleanField( initial=True, required=False, label=_("Autocomplete GSX repairs"), help_text=_("Complete the GSX repair when closing a Service Order") ) # start checkin fields checkin_enable = forms.BooleanField( initial=True, required=False, label=_("Enable check-in interface"), help_text=_("Uncheck to disable the check-in interface completely") ) checkin_user = forms.ModelChoiceField( required=False, label=_('User Account'), queryset=User.active.all(), help_text=_('User account to use for the public check-in service'), ) checkin_group = forms.ModelChoiceField( required=False, label=_('Group'), queryset=Group.objects.all(), help_text=_('Users to choose from in the check-in interface'), ) checkin_checklist = forms.ModelChoiceField( required=False, label=_('Checklist'), queryset=Checklist.objects.filter(enabled=True), help_text=_('Checklist to show during check-in'), ) checkin_queue = forms.ModelChoiceField( required=False, label=_('Queue'), queryset=Queue.objects.all(), help_text=_('Orders created through the check-in interface will go into this queue'), ) checkin_timeline = forms.BooleanField( initial=False, required=False, label=_('Show timeline'), help_text=_('Show status timeline on public repair status page'), ) checkin_password = forms.BooleanField( initial=False, required=False, label=_('Show password'), help_text=_('Make checkin device password field readable'), ) checkin_report_checklist = forms.BooleanField( initial=True, required=False, label=_('Show checklist results'), help_text=_('Show checklist results in order confirmation'), ) checkin_require_password = forms.BooleanField( initial=True, required=False, label=_('Require device password'), help_text=_('Check to make the "device password" field mandatory'), ) checkin_require_condition = forms.BooleanField( initial=True, required=False, label=_('Require device condition'), help_text=_('Check to make the "device condition" field mandatory'), ) # end checkin fields currency = forms.ChoiceField( label=_('Currency'), choices=( ('DKK', 'DKK'), ('EUR', 'EUR'), ('GBP', 'GBP'), ('NZD', 'NZD'), ('PLN', 'PLN'), ('SEK', 'SEK'), ('USD', 'USD'), ('ZAR', 'ZAR'), ), initial='EUR' ) gsx_account = forms.ModelChoiceField( required=False, label=_('Default account'), queryset=GsxAccount.objects.all(), help_text=_('Use this GSX account before and order is assigned to a queue') ) gsx_cert = forms.FileField( required=False, label=_('SSL certificate'), help_text=_('SSL client certificate for GSX connections') ) gsx_privkey = forms.FileField( required=False, label=_('SSL private key'), help_text=_('SSL private key for certificate') ) gsx_keypass = forms.CharField( required=False, widget=forms.PasswordInput, label=_('Private key passphrase'), help_text=_('Passphrase for private key') ) pct_margin = forms.CharField( required=False, max_length=128, label=_('Margin %'), help_text=_('Default margin for new products') ) pct_vat = forms.DecimalField( max_digits=4, required=False, label=_('VAT %'), help_text=_('Default VAT for new products') ) shipping_cost = forms.DecimalField( max_digits=4, required=False, label=_('Shipping Cost'), help_text=_('Default shipping cost for new products') ) track_inventory = forms.BooleanField( initial=True, required=False, label=_('Track inventory'), help_text=_('Unchecking this will disable tracking product amounts in your inventory') ) imap_host = forms.CharField( label=_('IMAP server'), max_length=128, required=False ) imap_user = forms.CharField( label=_('Username'), max_length=128, required=False ) imap_password = forms.CharField( max_length=128, label=_('Password'), widget=forms.PasswordInput(), required=False ) imap_ssl = forms.BooleanField(label=_('Use SSL'), initial=True, required=False) imap_act = forms.ModelChoiceField( required=False, label=_('User Account'), queryset=User.active.all(), help_text=_('User account to use when creating notes from messages'), ) default_sender = forms.ChoiceField( required=False, label=_('Default Sender'), choices=( ('user', _("User")), ('location', _("Location")), ('custom', _("Custom...")) ), help_text=_('Select the default sender address for outgoing emails') ) default_sender_custom = forms.EmailField( label=' ', required=False, widget=forms.TextInput(attrs={ 'placeholder': 'user@example.com', 'disabled': 'disabled' }) ) default_subject = forms.CharField( max_length=128, required=False, label=_('Default subject') ) smtp_host = forms.CharField( max_length=128, required=False, label=_('SMTP server') ) smtp_user = forms.CharField(max_length=128, required=False, label=_('Username')) smtp_password = forms.CharField( max_length=128, required=False, label=_('Password'), widget=forms.PasswordInput() ) ENCRYPTION_CHOICES = ( ('OFF', _('None')), ('SSL', 'SSL'), ('TLS', 'TLS'), ) smtp_encryption = forms.ChoiceField(choices=ENCRYPTION_CHOICES, initial='OFF', required=False, label=_('Encryption')) sms_gateway = forms.ChoiceField( label=_('SMS Gateway'), choices=( ('builtin', _('Built-in')), ('hqsms', 'HQSMS'), ('http', 'HTTP'), ('smtp', 'SMTP'), ('jazz', 'SMSjazz'), ), initial='http', required=False ) sms_smtp_address = forms.EmailField(required=False, label=_('Email address')) sms_http_url = forms.CharField( max_length=128, label=_('URL'), required=False, help_text=_('SMS Server URL'), initial='http://example.com:13013/cgi-bin/sendsms' ) sms_http_user = forms.CharField(max_length=128, label=_('Username'), required=False) sms_http_password = forms.CharField( max_length=128, required=False, label=_('Password'), widget=forms.PasswordInput() ) sms_http_sender = forms.CharField( max_length=128, required=False, label=_('Sender') ) sms_http_ssl = forms.BooleanField( required=False, label=_('Use SSL'), initial=True ) notify_location = forms.BooleanField( required=False, initial=True, label=_('Notify locations'), help_text=_("Daily reports will be sent to the location's email address") ) notify_address = forms.EmailField( required=False, label=_('Email address'), help_text=_("Send daily reports to this email address") ) def clean_notify_address(self, *args, **kwargs): """ Only validate notify_address if it was actually given """ from django.core.validators import validate_email address = self.cleaned_data.get('notify_address') if len(address): validate_email(address) return address def clean_pct_margin(self, *args, **kwargs): margin = self.cleaned_data.get('pct_margin') if re.match('^\d[\-=;\d]*\d$', margin): return margin raise forms.ValidationError(_('Invalid margin format')) def save(self, *args, **kwargs): config = dict() from django.conf import settings if self.cleaned_data.get('gsx_cert'): f = self.cleaned_data['gsx_cert'] with open(settings.GSX_CERT, 'wb+') as d: for chunk in f.chunks(): d.write(chunk) if self.cleaned_data.get('gsx_privkey'): f = self.cleaned_data['gsx_privkey'] with open(settings.GSX_KEY, 'wb+') as d: for chunk in f.chunks(): d.write(chunk) if self.cleaned_data.get('gsx_keypass'): from servo.lib.utils import strip_keypass keypass = self.cleaned_data['gsx_keypass'] strip_keypass(keypass, settings.GSX_KEY, settings.GSX_KEY) if self.cleaned_data.get('company_logo'): f = self.cleaned_data['company_logo'] target = 'uploads/logos/%s' % f.name with open(target, 'wb+') as d: for chunk in f.chunks(): d.write(chunk) self.cleaned_data['company_logo'] = 'logos/%s' % f.name else: # @fixme: make the form remember the previous value self.cleaned_data['company_logo'] = Configuration.get_company_logo() for k, v in self.cleaned_data.items(): field = Configuration.objects.get_or_create(key=k)[0] if re.search('password$', k) and v == '': v = field.value # don't save empty passwords if hasattr(v, 'pk'): v = v.pk # so we don't end up with object instances in the cache field.value = v or '' field.save() config[k] = v cache.set('config', config) return config