import logging import json from django import forms from django.utils.translation import gettext_lazy as _ from django.core.exceptions import ValidationError from soupsieve import select from pingcap_sso.models import ( ContactUs, ContactUsEmergency, ContactUsCooperation, ContactUsCommunication, OfficialContactUs, SecurityCode, User, CommunityContactUs, FXCRMJointSolutionMutualCertification, CaptchaStore, ) from pingcap_sso.user_behavior_track.models import TrackEvent from pingcap_sso.validators.phone import PhoneNumberValidator from pingcap_sso.utils import crm from pingcap_sso.utils.requests import get_client_ip, get_client_agent from pingcap_sso.utils.phone import is_virtual_phone from pingcap_sso.lib.upload import upload_file from pingcap_sso.lib.captcha import enable_captcha, verify_captcha from pingcap_sso.exceptions import CaptchaRequiredError logger = logging.getLogger(__name__) class ContactUsForm(forms.Form): openid = forms.CharField(required=False, max_length=64) page_url = forms.URLField(required=True) spend_time = forms.FloatField(required=True) name = forms.CharField(required=False, max_length=64) phone = forms.RegexField( required=False, regex=r'^1\d{10}$', error_messages={ 'invalid': _('invalid phone number'), 'required': _('phone number is required') }) company = forms.CharField(required=False, max_length=64) department = forms.CharField(required=False, max_length=64) wechat_number = forms.CharField(required=False, max_length=64) body = forms.CharField(required=False) def __init__(self, user, *args, **kwargs): self.user = user if user.is_authenticated else None super(ContactUsForm, self).__init__(*args, **kwargs) def clean_body(self): body = self.cleaned_data.get('body') if body: try: body = json.loads(body.replace("'", '"')) except Exception: raise forms.ValidationError('invalid body, required json(object)') else: body = {} return body def save(self): return ContactUs.objects.create( user_openid=self.cleaned_data.get('openid'), page_url=self.cleaned_data.get('page_url'), spend_time=self.cleaned_data.get('spend_time'), name=self.cleaned_data.get('name'), phone=self.cleaned_data.get('phone'), company=self.cleaned_data.get('company'), department=self.cleaned_data.get('department'), wechat_number=self.cleaned_data.get('wechat_number'), body=self.cleaned_data.get('body'), user=self.user ) class ContactUsEmergencyForm(forms.ModelForm): def __init__(self, user, *args, **kwargs): self.user = user super(ContactUsEmergencyForm, self).__init__(*args, **kwargs) def save(self): instance = super(ContactUsEmergencyForm, self).save(commit=False) instance.created_by_user = self.user instance.save() class Meta: model = ContactUsEmergency exclude = ['status'] class ContactUsCooperationForm(forms.ModelForm): def __init__(self, user, *args, **kwargs): self.user = user super(ContactUsCooperationForm, self).__init__(*args, **kwargs) def save(self): instance = super(ContactUsCooperationForm, self).save(commit=False) instance.created_by_user = self.user instance.save() class Meta: model = ContactUsCooperation exclude = ['status'] class MultiCharField(forms.Field): def __init__(self, *args, **kwargs): super(MultiCharField, self).__init__(*args, **kwargs) self.subfield = forms.CharField() def clean(self, value): if len(value) == 0 and self.required: raise ValidationError(self.error_messages["required"]) result = [] for i, v in enumerate(value): result.append(self.subfield.clean(value=v)) return result class ContactUsCommunicationForm(forms.ModelForm): def __init__(self, user, *args, **kwargs): self.user = user super(ContactUsCommunicationForm, self).__init__(*args, **kwargs) tidb_version = MultiCharField(required=True) tidb_usage = forms.MultipleChoiceField(choices=( ('htap', 'HTAP'), ('olap', 'OLAP'), ('olap_scale', 'OLAP Scale'), ('rawkv', 'RawKV') ), required=True) def save(self): instance = super(ContactUsCommunicationForm, self).save(commit=False) instance.created_by_user = self.user instance.save() class Meta: model = ContactUsCommunication exclude = ['status'] class OfficialContactUsForm(forms.ModelForm): job_title = forms.CharField(max_length=255, required=False) consult_category = forms.CharField(max_length=255, required=False) phone_code = forms.CharField(max_length=6) def __init__(self, request, user, *args, **kwargs): self.request = request if user and user.is_authenticated: self.user = user else: self.user = None super(OfficialContactUsForm, self).__init__(*args, **kwargs) def clean_phone(self): phone = self.cleaned_data.get('phone') if phone: PhoneNumberValidator()(phone) if is_virtual_phone(phone): raise ValidationError(_('currently does not support virtual carrier numbers')) return phone def clean_job_title(self): job_title = self.cleaned_data.get('job_title') if job_title and job_title not in crm.JOB_TITLE_OPTIONS: raise ValidationError(_('invalid option')) return job_title def clean_consult_category(self): consult_category = self.cleaned_data.get('consult_category') if consult_category and consult_category not in crm.CONSULT_CATEGORY_OPTIONS: raise ValidationError(_('invalid option')) return consult_category def clean(self): if self.errors: return phone = self.cleaned_data.get('phone') phone_code = self.cleaned_data.get('phone_code') if not SecurityCode.check_code( phone, phone_code, scene=SecurityCode.SCENE_OFFICIAL_CONTACT_US, ): self.add_error('phone_code', '手机验证码错误') return self.cleaned_data def save(self): data = self.cleaned_data instance = super(OfficialContactUsForm, self).save(commit=False) # find user by registered phone, associate this record with user if not self.user: try: self.user = User.objects.get(phone=data['phone']) except User.DoesNotExist: pass utm_source = data.get('utm_source') utm_medium = data.get('utm_medium') referer = data.get('referer') person_source, person_source_details = OfficialContactUs.get_person_source_detail( utm_source, utm_medium, referer ) instance.person_source = person_source instance.person_source_details = person_source_details instance.current_page = self.request.META.get('HTTP_REFERER', None) instance.user = self.user job_title = data.get('job_title') if job_title: instance.job_title_code = job_title instance.job_title_text = crm.JOB_TITLE_OPTIONS[job_title] consult_category = data.get('consult_category') if consult_category: instance.consult_category_code = consult_category instance.consult_category_text = crm.CONSULT_CATEGORY_OPTIONS[consult_category] instance.save() if self.user and self.user.is_authenticated: TrackEvent.objects.create( openid=self.user.openid, event_type='sso.submit_contact_us', event_body={}, user_agent=get_client_agent(self.request), ip=get_client_ip(self.request), description='在官网提交了联系我们', source=TrackEvent.SOURCE_SERVER ) class Meta: model = OfficialContactUs exclude = [ 'user', 'job_title_code', 'job_title_text', 'consult_category_code', 'consult_category_text', 'person_source', 'person_source_details', ] class OfficialJointSolutionMutualCertificationForm(forms.ModelForm): application_type = forms.CharField(max_length=100) joint_solution_industry = forms.CharField(max_length=100) phone_code = forms.CharField(max_length=6) attachment_docx = forms.FileField() attachment_xlsx = forms.FileField() def __init__(self, request, user, *args, **kwargs): self.request = request if user and user.is_authenticated: self.user = user else: self.user = None super(OfficialJointSolutionMutualCertificationForm, self).__init__(*args, **kwargs) def _clean_attachment(self, field_name, exts): file = self.cleaned_data.get(field_name) if file: try: file.size except Exception: import traceback logger.error(traceback.format_exc()) else: if file.size > 50 * 1 << 20: raise ValidationError('file size exceeds limit (50M)') if not file.name.endswith(exts): raise ValidationError('file type must be {}'.format(exts)) return file def clean_attachment_docx(self): return self._clean_attachment('attachment_docx', ('.doc', '.docx')) def clean_attachment_xlsx(self): return self._clean_attachment('attachment_xlsx', '.xlsx') def clean_company_address(self): company_address = self.cleaned_data.get('company_address') if company_address and company_address not in crm.CHINA_BUSINESS_PROVINCES: raise ValidationError(_('invalid option')) return company_address def clean_application_type(self): application_type = self.cleaned_data.get('application_type') if application_type and application_type not in crm.JOINT_SOLUTION_TYPE_OPTIONS: raise ValidationError(_('invalid option')) return application_type def clean_contact_phone(self): phone = self.cleaned_data.get('contact_phone') if phone: PhoneNumberValidator()(phone) if is_virtual_phone(phone): raise ValidationError(_('currently does not support virtual carrier numbers')) return phone def clean_joint_solution_industry(self): joint_solution_industry = self.cleaned_data.get('joint_solution_industry') if joint_solution_industry and joint_solution_industry not in crm.JOINT_SOLUTION_INDUSTRY_OPTIONS: raise ValidationError(_('invalid option')) return joint_solution_industry def clean(self): if self.errors: return phone = self.cleaned_data.get('contact_phone') phone_code = self.cleaned_data.get('phone_code') if not SecurityCode.check_code(phone, phone_code): self.add_error('phone_code', '手机验证码错误') return self.cleaned_data def save(self): data = self.cleaned_data instance = super(OfficialJointSolutionMutualCertificationForm, self).save(commit=False) # find user by registered phone, associate this record with user if not self.user: try: self.user = User.objects.get(phone=data['contact_phone']) except User.DoesNotExist: pass instance.user = self.user application_type = data.get('application_type') if application_type: instance.application_type_code = application_type instance.application_type_text = crm.JOINT_SOLUTION_TYPE_OPTIONS[application_type] joint_solution_industry = data.get('joint_solution_industry') if joint_solution_industry: instance.joint_solution_industry_code = joint_solution_industry instance.joint_solution_industry_text = crm.JOINT_SOLUTION_INDUSTRY_OPTIONS[joint_solution_industry] company_address = data.get('company_address') if company_address: for area_code, area_info in crm.CHINA_BUSINESS_AREA_MAP.items(): for province in area_info['provinces']: if province == company_address: instance.company_area_code = area_code instance.company_area_text = area_info['name'] break docx_file = upload_file(self.cleaned_data.get('attachment_docx'), self.user) xlsx_file = upload_file(self.cleaned_data.get('attachment_xlsx'), self.user) instance.attachment_1 = xlsx_file instance.attachment_2 = docx_file instance.save() if self.user and self.user.is_authenticated: TrackEvent.objects.create( openid=self.user.openid, event_type='sso.submit_official_joint_solution_mutual_certification', event_body={}, user_agent=get_client_agent(self.request), ip=get_client_ip(self.request), description='在官网提交了联合解决方案互认证申请', source=TrackEvent.SOURCE_SERVER ) class Meta: model = FXCRMJointSolutionMutualCertification exclude = ['created_at', 'user'] class CommunityContactUsForm(forms.ModelForm): phone = forms.RegexField( required=False, regex=r'^1\d{10}$', error_messages={ 'invalid': _('invalid phone number'), 'required': _('phone number is required') }) phone_code = forms.CharField(max_length=6, required=False) def __init__(self, request, user, *args, **kwargs): self.request = request if user and user.is_authenticated: self.user = user else: self.user = None super(CommunityContactUsForm, self).__init__(*args, **kwargs) def clean_phone(self): phone = self.cleaned_data.get('phone') if phone: PhoneNumberValidator()(phone) if is_virtual_phone(phone): raise ValidationError(_('currently does not support virtual carrier numbers')) return phone def clean(self): if self.errors: return phone = self.cleaned_data.get('phone') phone_code = self.cleaned_data.get('phone_code') if not self.user: if not phone: self.add_error('phone', 'phone number is required') return if not phone_code: self.add_error('phone_code', 'phone code is required') return if not SecurityCode.check_code(phone, phone_code): self.add_error('phone_code', '手机验证码错误') return return self.cleaned_data def save(self): data = self.cleaned_data instance = super(CommunityContactUsForm, self).save(commit=False) ip = get_client_ip(self.request) user_agent = get_client_agent(self.request) instance.ip = ip instance.user_agent = user_agent if self.user: instance.phone = self.user.phone else: try: self.user = User.objects.get(phone=data['phone']) except User.DoesNotExist: pass instance.user = self.user instance.save() if self.user: TrackEvent.objects.create( openid=self.user.openid, event_type='sso.submit_contact_us', event_body={}, user_agent=user_agent, ip=ip, description='在社区提交了联系我们', source=TrackEvent.SOURCE_SERVER ) class Meta: model = CommunityContactUs exclude = ['user', "created_at", "ip", "user_agent"] class OfficialContactUsSmsCodeForm(forms.Form): phone = forms.RegexField( required=True, regex=r'^1\d{10}$', error_messages={ 'invalid': _('invalid phone number'), 'required': _('phone number is required') } ) captcha_key = forms.CharField(required=False) captcha_response = forms.CharField(required=False) def clean_phone(self): phone = self.cleaned_data.get('phone') if phone: PhoneNumberValidator()(phone) if is_virtual_phone(phone): raise ValidationError(_('currently does not support virtual carrier numbers')) return phone def clean(self): if self.errors: return phone = self.cleaned_data.get('phone') captcha_key = self.cleaned_data.get('captcha_key') captcha_response = self.cleaned_data.get('captcha_response') if enable_captcha(phone): if not (captcha_key and captcha_response): raise CaptchaRequiredError(_('captcha key and response are required')) if not verify_captcha(captcha_key, captcha_response): self.add_error('captcha_response', _('invalid captcha response')) return return self.cleaned_data