__init__.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import copy
  2. import logging
  3. from django.core.exceptions import ValidationError
  4. from django.urls import reverse
  5. from django.utils.module_loading import import_string
  6. from django.utils.translation import ugettext as _
  7. from misago.conf import settings
  8. from .basefields import *
  9. from .serializers import serialize_profilefields_data
  10. logger = logging.getLogger('misago.users.ProfileFields')
  11. class ProfileFields(object):
  12. def __init__(self, fields_groups):
  13. self.is_loaded = False
  14. self.fields_groups = fields_groups
  15. self.fields_dict = {}
  16. def load(self):
  17. self.fields_dict = {}
  18. fieldnames = {}
  19. for group in self.fields_groups:
  20. for field_path in group['fields']:
  21. field = import_string(field_path)
  22. field._field_path = field_path
  23. if field_path in self.fields_dict:
  24. raise ValueError(
  25. "{} profile field has been specified twice".format(field._field_path)
  26. )
  27. if not getattr(field, 'fieldname', None):
  28. raise ValueError(
  29. "{} profile field has to specify fieldname attribute".format(
  30. field._field_path,
  31. )
  32. )
  33. if field.fieldname in fieldnames:
  34. raise ValueError(
  35. (
  36. '{} profile field defines fieldname "{}" '
  37. 'that is already in use by the {}'
  38. ).format(
  39. field._field_path,
  40. field.fieldname,
  41. fieldnames[field.fieldname],
  42. )
  43. )
  44. fieldnames[field.fieldname] = field_path
  45. self.fields_dict[field_path] = field()
  46. self.is_loaded = True
  47. def get_fields(self):
  48. if not self.is_loaded:
  49. self.load()
  50. return self.fields_dict.values()
  51. def get_fields_groups(self):
  52. if not self.is_loaded:
  53. self.load()
  54. groups = []
  55. for group in self.fields_groups:
  56. group_dict = {
  57. 'name': _(group['name']),
  58. 'fields': [],
  59. }
  60. for field_path in group['fields']:
  61. field = self.fields_dict[field_path]
  62. group_dict['fields'].append(field)
  63. if group_dict['fields']:
  64. groups.append(group_dict)
  65. return groups
  66. def add_fields_to_form(self, request, user, form):
  67. if not self.is_loaded:
  68. self.load()
  69. form._profile_fields = []
  70. for field in self.get_fields():
  71. if not field.is_editable(request, user):
  72. continue
  73. form._profile_fields.append(field.fieldname)
  74. form.fields[field.fieldname] = field.get_form_field(request, user)
  75. def add_fields_to_admin_form(self, request, user, form):
  76. self.add_fields_to_form(request, user, form)
  77. form._profile_fields_groups = []
  78. for group in self.fields_groups:
  79. group_dict = {
  80. 'name': _(group['name']),
  81. 'fields': [],
  82. }
  83. for field_path in group['fields']:
  84. field = self.fields_dict[field_path]
  85. if field.fieldname in form._profile_fields:
  86. group_dict['fields'].append(field.fieldname)
  87. if group_dict['fields']:
  88. form._profile_fields_groups.append(group_dict)
  89. def clean_form(self, request, user, form, cleaned_data):
  90. for field in self.get_fields():
  91. if field.fieldname not in cleaned_data:
  92. continue
  93. try:
  94. cleaned_data[field.fieldname] = field.clean(
  95. request, user, cleaned_data[field.fieldname])
  96. except ValidationError as e:
  97. form.add_error(field.fieldname, e)
  98. return cleaned_data
  99. def update_user_profile_fields(self, request, user, form):
  100. old_fields = copy.copy(user.profile_fields or {})
  101. new_fields = {}
  102. for fieldname in form._profile_fields:
  103. if fieldname in form.cleaned_data:
  104. new_fields[fieldname] = form.cleaned_data[fieldname]
  105. user.profile_fields = new_fields
  106. old_fields_reduced = {k: v for k, v in old_fields.items() if v}
  107. new_fields_reduced = {k: v for k, v in new_fields.items() if v}
  108. if old_fields_reduced != new_fields_reduced:
  109. self.log_profile_fields_update(request, user)
  110. def log_profile_fields_update(self, request, user):
  111. if request.user == user:
  112. log_message = "{} edited own profile fields".format(user.username)
  113. else:
  114. log_message = "{} edited {}'s (#{}) profile fields".format(request.user, user.username, user.pk)
  115. logger.info(
  116. log_message,
  117. extra={
  118. 'absolute_url': request.build_absolute_uri(
  119. reverse(
  120. 'misago:user-details',
  121. kwargs={
  122. 'slug': user.slug,
  123. 'pk': user.pk,
  124. },
  125. )
  126. ),
  127. }
  128. )
  129. def search_users(self, criteria, queryset):
  130. if not self.is_loaded:
  131. self.load()
  132. q_obj = None
  133. for field in self.fields_dict.values():
  134. q = field.search_users(criteria)
  135. if q:
  136. if q_obj:
  137. q_obj = q_obj | q
  138. else:
  139. q_obj = q
  140. if q_obj:
  141. return queryset.filter(q_obj)
  142. return queryset
  143. profilefields = ProfileFields(settings.MISAGO_PROFILE_FIELDS)