33from django import forms
44from django .conf import settings
55from django .contrib .auth .models import Group
6+ from django .db .models .functions import Coalesce
67from django .template .defaultfilters import slugify
78from django .utils .translation import gettext_lazy as _lazy
89from django .utils .translation import ngettext_lazy as _nlazy
910
1011from kitsune .products .models import Product , Topic
1112from kitsune .sumo .form_fields import MultiUsernameField , MultiUsernameFilterField
12- from kitsune .wiki .config import CATEGORIES , SIGNIFICANCES
13+ from kitsune .wiki .config import (
14+ CANNED_RESPONSES_CATEGORY ,
15+ CATEGORIES ,
16+ SIGNIFICANCES ,
17+ TEMPLATES_CATEGORY ,
18+ )
1319from kitsune .wiki .content_managers import ManualContentManager
1420from kitsune .wiki .models import MAX_REVISION_COMMENT_LENGTH , Document , DraftRevision , Revision
1521from kitsune .wiki .tasks import add_short_links
6369)
6470PRODUCT_REQUIRED = _lazy ("Please select at least one product." )
6571TOPIC_REQUIRED = _lazy ("Please select at least one topic." )
72+ RELATED_DOCUMENTS_DISALLOWED = _lazy (
73+ "Related documents are not allowed for Templates and Canned Responses."
74+ )
6675
6776
6877class DocumentForm (forms .ModelForm ):
@@ -135,8 +144,11 @@ class DocumentForm(forms.ModelForm):
135144 widget = ProductsWidget (),
136145 )
137146
138- related_documents = forms .MultipleChoiceField (
139- label = _lazy ("Related documents:" ), required = False , widget = RelatedDocumentsWidget ()
147+ related_documents = forms .ModelMultipleChoiceField (
148+ required = False ,
149+ queryset = Document .objects .none (),
150+ label = _lazy ("Related documents:" ),
151+ widget = RelatedDocumentsWidget (),
140152 )
141153
142154 locale = forms .CharField (widget = forms .HiddenInput ())
@@ -154,10 +166,28 @@ def clean_slug(self):
154166 raise forms .ValidationError (SLUG_INVALID )
155167 return slug
156168
169+ def clean_related_documents (self ):
170+ """Replace any children with their parent documents."""
171+ related_docs = self .cleaned_data .get ("related_documents" , Document .objects .none ())
172+
173+ # In the form, we'll show the translations of the related documents if they exist
174+ # (see kitsune.wiki.views.get_visible_related_documents), but we always store
175+ # related documents as parents.
176+ return Document .objects .filter (
177+ id__in = related_docs .annotate (
178+ root_id = Coalesce ("parent_id" , "id" ),
179+ ).values_list ("root_id" , flat = True )
180+ ).distinct ()
181+
157182 def clean (self ):
158183 cdata = super ().clean ()
159184 locale = cdata .get ("locale" )
160185
186+ if cdata .get ("related_documents" ) and (
187+ int (cdata .get ("category" , 0 )) in (TEMPLATES_CATEGORY , CANNED_RESPONSES_CATEGORY )
188+ ):
189+ raise forms .ValidationError (RELATED_DOCUMENTS_DISALLOWED )
190+
161191 # Products are required for en-US
162192 product_ids = set (map (int , cdata .get ("products" , [])))
163193 if locale == settings .WIKI_DEFAULT_LANGUAGE and (not product_ids or len (product_ids ) < 1 ):
@@ -229,6 +259,7 @@ def __init__(self, *args, **kwargs):
229259 can_archive = kwargs .pop ("can_archive" , False )
230260 can_edit_needs_change = kwargs .pop ("can_edit_needs_change" , False )
231261 initial_title = kwargs .pop ("initial_title" , "" )
262+ locale = kwargs .pop ("locale" , None )
232263
233264 super ().__init__ (* args , ** kwargs )
234265
@@ -244,8 +275,13 @@ def __init__(self, *args, **kwargs):
244275 products_field = self .fields ["products" ]
245276 products_field .choices = Product .active .values_list ("id" , "title" )
246277
278+ # Set up the queryset for the related documents field.
247279 related_documents_field = self .fields ["related_documents" ]
248- related_documents_field .choices = Document .objects .values_list ("id" , "title" )
280+ queryset = Document .objects .filter (is_template = False )
281+ locales = [settings .WIKI_DEFAULT_LANGUAGE ]
282+ if locale and (locale != settings .WIKI_DEFAULT_LANGUAGE ):
283+ locales .append (locale )
284+ related_documents_field .queryset = queryset .filter (locale__in = locales )
249285
250286 # If user hasn't permission to frob is_archived, remove the field. This
251287 # causes save() to skip it as well.
0 commit comments