PageRenderTime 63ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/karaage/plugins/kgapplications/forms.py

https://github.com/Karaage-Cluster/karaage
Python | 431 lines | 393 code | 21 blank | 17 comment | 8 complexity | ae3fbd894ac46776c06f28fad8673d63 MD5 | raw file
  1. # Copyright 2010-2017, The University of Melbourne
  2. # Copyright 2010-2017, Brian May
  3. #
  4. # This file is part of Karaage.
  5. #
  6. # Karaage is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # Karaage is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with Karaage If not, see <http://www.gnu.org/licenses/>.
  18. import ajax_select.fields
  19. import six
  20. from captcha.fields import CaptchaField
  21. from django import forms
  22. from django.conf import settings
  23. from django.db.models import Q
  24. from django.urls import reverse
  25. from django.utils.safestring import mark_safe
  26. from karaage.common.forms import (
  27. clean_email,
  28. validate_password,
  29. validate_phone_number,
  30. )
  31. from karaage.institutes.models import Institute
  32. from karaage.people.models import Person
  33. from karaage.people.utils import (
  34. UsernameException,
  35. validate_username_for_new_person,
  36. )
  37. from karaage.projects.models import Project
  38. from .models import Applicant, ProjectApplication
  39. APP_CHOICES = (
  40. ('U', 'Join an existing project'),
  41. )
  42. if settings.ALLOW_NEW_PROJECTS:
  43. APP_CHOICES = APP_CHOICES + (
  44. ('P', 'Apply to start a new project'),
  45. )
  46. class StartApplicationForm(forms.Form):
  47. application_type = forms.ChoiceField(
  48. choices=APP_CHOICES, widget=forms.RadioSelect())
  49. class ApplicantForm(forms.ModelForm):
  50. username = forms.RegexField(
  51. "^%s$" % settings.USERNAME_VALIDATION_RE,
  52. label=six.u("Requested username"),
  53. max_length=settings.USERNAME_MAX_LENGTH,
  54. help_text=(settings.USERNAME_VALIDATION_ERROR_MSG
  55. + " and has a max length of %s." %
  56. settings.USERNAME_MAX_LENGTH))
  57. telephone = forms.CharField(
  58. required=True,
  59. label=six.u("Office Telephone"),
  60. help_text=six.u(
  61. "Used for emergency contact and password reset service."),
  62. validators=[validate_phone_number],
  63. )
  64. mobile = forms.CharField(
  65. required=False,
  66. validators=[validate_phone_number],
  67. )
  68. fax = forms.CharField(
  69. required=False,
  70. validators=[validate_phone_number],
  71. )
  72. class Meta:
  73. model = Applicant
  74. fields = [
  75. 'email', 'username', 'title',
  76. 'short_name', 'full_name', 'institute', 'department',
  77. 'position', 'telephone', 'mobile', 'supervisor',
  78. 'address', 'city', 'postcode', 'country', 'fax',
  79. ]
  80. def clean(self):
  81. data = super(ApplicantForm, self).clean()
  82. for key in [
  83. 'short_name', 'full_name', 'email', 'position',
  84. 'supervisor', 'department', 'telephone', 'mobile', 'fax',
  85. 'address', 'city', 'postcode', ]:
  86. if key in data and data[key]:
  87. data[key] = data[key].strip()
  88. return data
  89. def __init__(self, *args, **kwargs):
  90. super(ApplicantForm, self).__init__(*args, **kwargs)
  91. self.fields['title'].required = True
  92. self.fields['short_name'].required = True
  93. self.fields['short_name'].help_text = \
  94. "This is typically your given name. "\
  95. "For example enter 'Fred' here."
  96. self.fields['full_name'].required = True
  97. self.fields['full_name'].help_text = \
  98. "This is typically your full name. " \
  99. "For example enter 'Fred Smith' here."
  100. self.fields['username'].label = 'Requested username'
  101. self.fields['username'].required = True
  102. self.fields['institute'].required = True
  103. self.fields['institute'].help_text = \
  104. "If your institute is not listed please contact %s"\
  105. % settings.ACCOUNTS_EMAIL
  106. self.fields['department'].required = True
  107. def clean_username(self):
  108. username = self.cleaned_data['username']
  109. if username:
  110. try:
  111. validate_username_for_new_person(username)
  112. except UsernameException as e:
  113. raise forms.ValidationError(e.args[0])
  114. return username
  115. def clean_email(self):
  116. email = self.cleaned_data['email']
  117. users = Person.objects.filter(email__exact=email)
  118. if users.count() > 0:
  119. raise forms.ValidationError(
  120. six.u(
  121. 'An account with this email already exists. '
  122. 'Please email %s')
  123. % settings.ACCOUNTS_EMAIL)
  124. clean_email(email)
  125. return email
  126. class UserApplicantForm(ApplicantForm):
  127. institute = forms.ModelChoiceField(queryset=None, required=True)
  128. def __init__(self, *args, **kwargs):
  129. super(UserApplicantForm, self).__init__(*args, **kwargs)
  130. self.fields['institute'].queryset = Institute.active.filter(
  131. Q(saml_entityid="") | Q(saml_entityid__isnull=True))
  132. def save(self, commit=True):
  133. applicant = super(UserApplicantForm, self).save(commit=commit)
  134. if commit:
  135. applicant.save()
  136. return applicant
  137. class Meta:
  138. model = Applicant
  139. exclude = ['email']
  140. class AafApplicantForm(UserApplicantForm):
  141. def __init__(self, *args, **kwargs):
  142. super(AafApplicantForm, self).__init__(*args, **kwargs)
  143. del self.fields['institute']
  144. class Meta:
  145. model = Applicant
  146. exclude = ['email', 'institute']
  147. class CommonApplicationForm(forms.ModelForm):
  148. aup = forms.BooleanField(
  149. error_messages={'required': 'You must accept to proceed.'})
  150. application_type = forms.ChoiceField(
  151. choices=APP_CHOICES, widget=forms.RadioSelect())
  152. def __init__(self, *args, **kwargs):
  153. super(CommonApplicationForm, self).__init__(*args, **kwargs)
  154. aup_url = getattr(settings, 'AUP_URL', None)
  155. if aup_url is None:
  156. aup_url = reverse('kg_aup')
  157. self.fields['aup'].label = mark_safe(six.u(
  158. 'I have read and agree to the '
  159. '<a href="%s" target="_blank">Acceptable Use Policy</a>')
  160. % aup_url)
  161. class Meta:
  162. model = ProjectApplication
  163. fields = ['needs_account']
  164. class NewProjectApplicationForm(forms.ModelForm):
  165. name = forms.CharField(
  166. label="Project Title", widget=forms.TextInput(attrs={'size': 60}))
  167. description = forms.CharField(
  168. max_length=1000,
  169. widget=forms.Textarea(attrs={
  170. 'class': 'vLargeTextField', 'rows': 10, 'cols': 40}))
  171. additional_req = forms.CharField(
  172. label="Additional requirements",
  173. widget=forms.Textarea(attrs={
  174. 'class': 'vLargeTextField', 'rows': 10, 'cols': 40}),
  175. help_text=six.u("Do you have any special requirements?"),
  176. required=False)
  177. def __init__(self, *args, **kwargs):
  178. super(NewProjectApplicationForm, self).__init__(*args, **kwargs)
  179. class Meta:
  180. model = ProjectApplication
  181. fields = [
  182. 'name', 'description', 'additional_req',
  183. ]
  184. class ExistingProjectApplicationForm(forms.ModelForm):
  185. project = forms.ModelChoiceField(queryset=None)
  186. def __init__(self, *args, **kwargs):
  187. super(ExistingProjectApplicationForm, self).__init__(*args, **kwargs)
  188. self.fields['project'].queryset = Project.active.all()
  189. class Meta:
  190. model = ProjectApplication
  191. fields = ['project']
  192. class InviteUserApplicationForm(forms.ModelForm):
  193. email = forms.EmailField()
  194. def __init__(self, *args, **kwargs):
  195. self.cleaned_data = None
  196. self.fields = None
  197. super(InviteUserApplicationForm, self).__init__(*args, **kwargs)
  198. self.fields['email'].required = True
  199. self.fields['header_message'].required = True
  200. class Meta:
  201. model = ProjectApplication
  202. fields = ['email', 'make_leader', 'header_message']
  203. def clean_email(self):
  204. email = self.cleaned_data['email']
  205. clean_email(email)
  206. return email
  207. class UnauthenticatedInviteUserApplicationForm(forms.Form):
  208. email = forms.EmailField()
  209. captcha = CaptchaField(
  210. label=six.u('CAPTCHA'),
  211. help_text=six.u("Please enter the text displayed in the image above."))
  212. def clean_email(self):
  213. email = self.cleaned_data['email']
  214. query = Person.active.filter(email=email)
  215. if query.count() > 0:
  216. raise forms.ValidationError(six.u(
  217. 'E-Mail address is already in use. '
  218. 'If you already have an account, please login.'))
  219. clean_email(email)
  220. return email
  221. def approve_project_form_generator(application, auth):
  222. if application.project is None:
  223. # new project
  224. include_fields = [
  225. 'additional_req', 'needs_account']
  226. else:
  227. # existing project
  228. include_fields = [
  229. 'make_leader', 'needs_account']
  230. class ApproveProjectForm(forms.ModelForm):
  231. if application.project is None:
  232. # new project
  233. additional_req = forms.CharField(
  234. label="Additional requirements",
  235. widget=forms.Textarea(attrs={
  236. 'class': 'vLargeTextField', 'rows': 10, 'cols': 40}),
  237. help_text=six.u("Do you have any special requirements?"),
  238. required=False)
  239. class Meta:
  240. model = ProjectApplication
  241. fields = include_fields
  242. def __init__(self, *args, **kwargs):
  243. super(ApproveProjectForm, self).__init__(*args, **kwargs)
  244. self.fields['needs_account'].label = \
  245. six.u("Does this person require a cluster account?")
  246. self.fields['needs_account'].help_text = \
  247. six.u("Will this person be working on the project?")
  248. return ApproveProjectForm
  249. def admin_approve_project_form_generator(application, auth):
  250. parent = approve_project_form_generator(application, auth)
  251. if application.project is None:
  252. # new project
  253. include_fields = [
  254. 'pid', 'additional_req', 'needs_account']
  255. else:
  256. # existing project
  257. include_fields = ['make_leader', 'needs_account']
  258. class AdminApproveProjectForm(parent):
  259. if application.project is None:
  260. # new project
  261. additional_req = forms.CharField(
  262. label="Additional requirements",
  263. widget=forms.Textarea(attrs={
  264. 'class': 'vLargeTextField', 'rows': 10, 'cols': 40}),
  265. help_text=six.u("Do you have any special requirements?"),
  266. required=False)
  267. pid = forms.RegexField(
  268. "^%s$" % settings.PROJECT_VALIDATION_RE, required=False,
  269. label='PID', help_text='Leave blank for auto generation',
  270. error_messages={
  271. 'invalid': settings.PROJECT_VALIDATION_ERROR_MSG})
  272. class Meta:
  273. model = ProjectApplication
  274. fields = include_fields
  275. def clean_pid(self):
  276. pid = self.cleaned_data['pid']
  277. if not pid:
  278. return pid
  279. try:
  280. Institute.objects.get(name=pid)
  281. raise forms.ValidationError(
  282. six.u('Project ID already in system'))
  283. except Institute.DoesNotExist:
  284. pass
  285. try:
  286. Project.objects.get(pid=pid)
  287. raise forms.ValidationError(
  288. six.u('Project ID already in system'))
  289. except Project.DoesNotExist:
  290. pass
  291. return pid
  292. return AdminApproveProjectForm
  293. class PersonSetPassword(forms.Form):
  294. """
  295. A form that lets a user change set his/her password without entering the
  296. old password
  297. """
  298. new_password1 = forms.CharField(label=six.u("New password"),
  299. widget=forms.PasswordInput)
  300. new_password2 = forms.CharField(label=six.u("New password confirmation"),
  301. widget=forms.PasswordInput)
  302. def __init__(self, person, *args, **kwargs):
  303. self.person = person
  304. super(PersonSetPassword, self).__init__(*args, **kwargs)
  305. def clean_new_password2(self):
  306. password1 = self.cleaned_data.get('new_password1')
  307. password2 = self.cleaned_data.get('new_password2')
  308. return validate_password(self.person.username, password1, password2)
  309. def save(self, commit=True):
  310. self.person.set_password(self.cleaned_data['new_password1'])
  311. if commit:
  312. self.person.save()
  313. return self.person
  314. class PersonVerifyPassword(forms.Form):
  315. """
  316. A form that lets a user verify his old password and updates it on all
  317. datastores.
  318. """
  319. password = forms.CharField(
  320. label="Existing password", widget=forms.PasswordInput)
  321. def __init__(self, person, *args, **kwargs):
  322. self.person = person
  323. super(PersonVerifyPassword, self).__init__(*args, **kwargs)
  324. def clean_password(self):
  325. password = self.cleaned_data['password']
  326. person = Person.objects.authenticate(
  327. username=self.person.username, password=password)
  328. if person is None:
  329. raise forms.ValidationError(six.u("Password is incorrect."))
  330. assert person == self.person
  331. if not person.is_active or person.is_locked():
  332. raise forms.ValidationError(six.u("Person is locked."))
  333. return password
  334. def save(self, commit=True):
  335. password = self.cleaned_data['password']
  336. self.person.set_password(password)
  337. if commit:
  338. self.person.save()
  339. return self.person
  340. class ApplicantReplace(forms.Form):
  341. replace_applicant = ajax_select.fields.AutoCompleteSelectField(
  342. 'person', required=True,
  343. help_text="Do not set unless absolutely positive sure.")
  344. def __init__(self, application, *args, **kwargs):
  345. self.application = application
  346. super(ApplicantReplace, self).__init__(*args, **kwargs)
  347. def save(self, *args, **kwargs):
  348. replace_applicant = self.cleaned_data['replace_applicant']
  349. if replace_applicant is not None:
  350. self.application.new_applicant = None
  351. self.application.existing_person = replace_applicant
  352. self.application.save()