def render_cosinnus_topics_field(escape_html=None): topics = CommaSeparatedSelect2MultipleChoiceField(choices=TAG_OBJECT.TOPIC_CHOICES, required=False, widget=CommaSeparatedSelect2MultipleWidget(select2_options={'closeOnSelect': 'true'})) topics_field_name = 'topics' topics_field_value = None topics_html = topics.widget.render(topics_field_name, topics_field_value, {'id': 'id_topics', 'placeholder': _('Topics')}) topics_html = topics_html.replace('\r', '').replace('\n', '') if escape_html: topics_html = escape(topics_html) return topics_html
class BaseTagObjectForm(GroupKwargModelFormMixin, UserKwargModelFormMixin, forms.ModelForm): like = forms.BooleanField(label=_('Like'), required=False) approach = Select2ChoiceField(choices=TagObject.APPROACH_CHOICES, required=False) topics = CommaSeparatedSelect2MultipleChoiceField( choices=TagObject.TOPIC_CHOICES, required=False) visibility = Select2ChoiceField( choices=TagObject.VISIBILITY_CHOICES, required=False, widget=Select2Widget(select2_options={'allowClear': False}) ) # the widget currently ignores the allowClear setting! # persons = will be defined in __init__ class Meta(object): model = TagObject exclude = ( 'group', 'likes', 'likers', ) widgets = { 'location_lat': forms.HiddenInput(), 'location_lon': forms.HiddenInput(), } def __init__(self, *args, **kwargs): """ Initialize and populate the select2 tags field """ super(BaseTagObjectForm, self).__init__(*args, **kwargs) # needs to be initialized here because using reverser_lazy() at model instantiation time causes problems self.fields['tags'] = TagSelect2Field( required=False, data_url=reverse_lazy('cosinnus:select2:tags')) # inherit tags from group for new TaggableObjects preresults = [] if self.instance.pk: preresults = self.instance.tags.values_list('name', 'name').all() elif self.group: preresults = self.group.media_tag.tags.values_list('name', 'name').all() if preresults: self.fields['tags'].choices = preresults self.fields['tags'].initial = [ key for key, val in preresults ] #[tag.name for tag in self.instance.tags.all()] self.initial['tags'] = self.fields['tags'].initial # if no media tag data was supplied the object was created directly and not through a frontend form # we then manually inherit the group's media_tag topics by adding them to the data # (usually they would have been added into the form's initial data) if self.data and not any( [key.startswith('media_tag-') for key in list(self.data.keys())] ) and self.group and self.group.media_tag and self.group.media_tag.topics: self.data._mutable = True self.data.setlist('media_tag-topics', self.group.media_tag.topics.split(',')) if self.group and not self.instance.pk: # for new TaggableObjects (not groups), set the default visibility corresponding to the group's public status self.fields[ 'visibility'].initial = get_inherited_visibility_from_group( self.group) if self.group: data_url = group_aware_reverse('cosinnus:select2:group-members', kwargs={'group': self.group}) else: data_url = reverse('cosinnus:select2:all-members') # override the default persons field with select2 #self.fields['persons'] = HeavySelect2MultipleChoiceField(label=_("Persons"), help_text='', required=False, data_url=data_url) self.fields['persons'] = UserSelect2MultipleChoiceField( label=_("Persons"), help_text='', required=False, data_url=data_url) if self.instance.pk: # choices and initial must be set so pre-existing form fields can be prepopulated preresults = get_user_select2_pills(self.instance.persons.all(), text_only=False) self.fields['persons'].choices = preresults self.fields['persons'].initial = [key for key, val in preresults] self.initial['persons'] = self.fields['persons'].initial if self.group and self.group.media_tag_id: group_media_tag = self.group.media_tag if group_media_tag and group_media_tag is not self.instance: # We must only take data from the group's media tag iff we are # working on a TaggableObjectModel, not on a group opts = self._meta # 1. Use all the data from the group's media tag # 2. Use the explicitly defined initial data (self.initial) and # override the data from the group media tag # 3. Set the combined data as new initial data group_data = forms.model_to_dict(group_media_tag, opts.fields, opts.exclude) group_data.update(self.initial) old_initial = self.initial self.initial = group_data # the default visibility corresponds to group's public setting if 'visibility' in old_initial: self.initial['visibility'] = old_initial['visibility'] else: self.initial[ 'visibility'] = get_inherited_visibility_from_group( self.group) if (self.user and self.instance.pk and self.instance.likers.filter(id=self.user.id).exists()): self.fields['like'].initial = True # use select2 widgets for m2m fields for field in [ self.fields['text_topics'], ]: if type(field.widget) is SelectMultiple: field.widget = Select2MultipleWidget(choices=field.choices) # since the widget currently ignores the allowClear setting we have # to use this hack to remove the clear-button self.fields['visibility'].widget.is_required = True # save BBB room if self.instance.pk and self.instance.bbb_room: self.initial['bbb_room'] = self.instance.bbb_room def save(self, commit=True): self.instance = super(BaseTagObjectForm, self).save(commit=False) # restore BBB room if 'bbb_room' in self.initial: self.instance.bbb_room = self.initial['bbb_room'] # the tag inherits the visibility default from the instance's group # if no or no valid visibility has been selected in the form, visibility_data_value = get_int_or_None( self.cleaned_data.get('visibility', None)) if visibility_data_value not in BaseTagObject.VISIBILITY_VALID_VALUES: # check if our tag object belongs to a group (i.e: isn't itself a group, or a user): if hasattr(self.instance, 'group') and self.instance.group: self.instance.visibility = get_inherited_visibility_from_group( self.instance.group) if self.user: if not self.instance.pk: # We need to save the tag here to allow add/remove of the user self.instance.save() if self.cleaned_data.get('like', False): self.instance.likers.add(self.user) else: self.instance.likers.remove(self.user) # like count is updated in model.save() if commit: self.instance.save() # For future reference: we skip the call to `save_m2m` here, because the django-multiform `MultiModelForm` already calls it! # This should be safe, but maybe in a bug corner case it could cause the `save_m2m` call to be skipped entirely # self.save_m2m() return self.instance
class TaggableModelSearchForm(SearchForm): """ This is almost the same search form as shipped with django-haystack except it limits the choices to models that are a subclasses of the :class:`~cosinnus.models.BaseTaggableObjectModel`. """ MAX_RESULTS = 200 groups = forms.ChoiceField(label=_('Limit to teams'), required=False, initial='all', choices=(('all', _('All')), ('mine', _('My teams')), ('others', _('Other teams'))), widget=forms.RadioSelect) models = forms.MultipleChoiceField(required=False) topics = CommaSeparatedSelect2MultipleChoiceField(required=False, choices=BaseTagObject.TOPIC_CHOICES, widget=CommaSeparatedSelect2MultipleWidget(select2_options={'closeOnSelect': 'true'})) location = forms.CharField(required=False) valid_start = forms.DateField(required=False) valid_end = forms.DateField(required=False) def __init__(self, *args, **kwargs): self.request = kwargs.pop('request') super(TaggableModelSearchForm, self).__init__(*args, **kwargs) self.fields['models'].choices = list(MODEL_ALIASES.items()) def get_models(self): """ Return the models of types user has selected to filter search on """ search_models = [] if self.is_valid(): # we either use the models given to us from the form, or if empty, # all models available for search model_aliases_query = self.cleaned_data.get('models', []) if not model_aliases_query: model_aliases_query = list(MODEL_ALIASES.keys()) for model_alias in model_aliases_query: if model_alias in list(MODEL_ALIASES.keys()): model_string = MODEL_ALIASES[model_alias] if model_string == '<userprofile>': model = get_user_profile_model() else: model = apps.get_model(*model_string.split('.')) search_models.append(model) return search_models def search(self): sqs = super(TaggableModelSearchForm, self).search() if hasattr(self, 'cleaned_data'): sqs = filter_searchqueryset_for_read_access(sqs, self.request.user) sqs = filter_searchqueryset_for_portal(sqs) sqs = self._filter_group_selection(sqs) sqs = self._filter_media_tags(sqs) if self.cleaned_data.get('q', None): sqs = self._boost_search_query(sqs) if self.request.GET.get('o', None) == 'newest': sqs = sqs.order_by('-created', '-_score') ret = sqs.models(*self.get_models()) ret = ret[:self.MAX_RESULTS] return ret def no_query_found(self): """ Overriding default behaviour to allow topic searches without textual query. """ if hasattr(self, 'cleaned_data') and self.cleaned_data.get('topics', None): return self.searchqueryset.all() return EmptySearchQuerySet() def _boost_search_query(self, sqs): q = self.cleaned_data['q'] sqs = sqs.filter(SQ(boosted=AutoQuery(q)) | SQ(text=AutoQuery(q))) return sqs def _filter_group_selection(self, sqs): """ Checks the request for an query parameter ``groups`` which can be one of ``all`` Don't filter on groups (except the respective permissions) ``mine`` Only include results of groups the current user is a member of ``others`` Only include results of groups the current user is not a member of Any other value will be interpreted as ``all``. """ user = self.request.user if user.is_authenticated: term = self.cleaned_data.get('groups', 'all').lower() if term == 'mine': sqs = sqs.filter_and(group_members__contains=user.id) elif term == 'others': sqs = sqs.exclude(group_members__contains=user.id) else: pass # we don't need to limit the result set on anonymous user. They are not # a member of any group. return sqs def _filter_media_tags(self, sqs): topics = ensure_list_of_ints(self.cleaned_data.get('topics', [])) if topics: sqs = sqs.filter_and(mt_topics__in=topics) location = self.cleaned_data.get('location', None) if location: sqs = sqs.filter_and(mt_location__contains=location.lower()) start = self.cleaned_data.get('valid_start', None) if start: sqs = sqs.filter_and(mt_valid_start__gte=start) end = self.cleaned_data.get('valid_end', None) if end: sqs = sqs.filter_and(mt_valid_end__gte=end) return sqs