class RevisionDashboardForm(forms.Form): locale = forms.ChoiceField( choices=LANG_CHOICES, # Required for non-translations, which is # enforced in Document.clean(). required=False, label=_lazy(u'Locale:')) user = StrippedCharField(min_length=1, max_length=255, required=False, label=_lazy(u'User:'******'Topic:')) start_date = forms.DateField( required=False, label=_lazy(u'Start Date:'), input_formats=['%m/%d/%Y'], widget=forms.TextInput(attrs={'pattern': '\d{1,2}/\d{1,2}/\d{4}'})) end_date = forms.DateField( required=False, label=_lazy(u'End Date:'), input_formats=['%m/%d/%Y'], widget=forms.TextInput(attrs={'pattern': '\d{1,2}/\d{1,2}/\d{4}'}))
class TreeMoveForm(forms.Form): title = StrippedCharField( min_length=1, max_length=255, required=False, widget=forms.TextInput(attrs={'placeholder': TITLE_PLACEHOLDER}), label=_lazy(u'Title:'), help_text=_lazy(u'Title of article'), error_messages={ 'required': TITLE_REQUIRED, 'min_length': TITLE_SHORT, 'max_length': TITLE_LONG }) slug = StrippedCharField(min_length=1, max_length=255, widget=forms.TextInput(), label=_lazy(u'New slug:'), help_text=_lazy(u'New article URL'), error_messages={ 'required': SLUG_REQUIRED, 'min_length': SLUG_SHORT, 'max_length': SLUG_LONG }) def clean_slug(self): # Removes leading slash and {locale/docs/} if necessary # IMPORTANT: This exact same regex is used on the client side, so # update both if doing so self.cleaned_data['slug'] = re.sub(re.compile(SLUG_CLEANSING_REGEX), '', self.cleaned_data['slug']) return self.cleaned_data['slug']
class ExampleForm(forms.Form): """Example form to test a bunch of Django fields.""" char = forms.CharField(max_length=10) char_optional = forms.CharField(required=False, widget=forms.TextInput()) file = forms.FileField(max_length=10) choice = forms.ChoiceField(choices=((1, 1), (2, 2))) stripped_char = StrippedCharField(max_length=10) bool = forms.BooleanField() textarea = StrippedCharField(widget=forms.Textarea()) email = forms.EmailField() url = forms.URLField(required=False, verify_exists=False) date = forms.DateField() time = forms.TimeField()
class TreeMoveForm(forms.Form): title = StrippedCharField( min_length=1, max_length=255, required=False, widget=forms.TextInput(attrs={'placeholder': TITLE_PLACEHOLDER}), label=_lazy(u'Title:'), help_text=_lazy(u'Title of article'), error_messages={ 'required': TITLE_REQUIRED, 'min_length': TITLE_SHORT, 'max_length': TITLE_LONG }) slug = StrippedCharField(min_length=1, max_length=255, widget=forms.TextInput(), label=_lazy(u'New slug:'), help_text=_lazy(u'New article URL'), error_messages={ 'required': SLUG_REQUIRED, 'min_length': SLUG_SHORT, 'max_length': SLUG_LONG }) locale = StrippedCharField(min_length=2, max_length=5, widget=forms.HiddenInput()) def clean_slug(self): # We only want the slug here; inputting a full URL would lead # to disaster. if '://' in self.cleaned_data['slug']: raise forms.ValidationError('Please enter only the slug to move ' 'to, not the full URL.') # Removes leading slash and {locale/docs/} if necessary # IMPORTANT: This exact same regex is used on the client side, so # update both if doing so self.cleaned_data['slug'] = re.sub(re.compile(SLUG_CLEANSING_REGEX), '', self.cleaned_data['slug']) return self.cleaned_data['slug'] def clean(self): cleaned_data = super(TreeMoveForm, self).clean() if set(['slug', 'locale']).issubset(cleaned_data): slug, locale = cleaned_data['slug'], cleaned_data['locale'] try: valid_slug_parent(slug, locale) except Exception, e: raise forms.ValidationError(e.args[0]) return cleaned_data
class NewThreadForm(forms.Form): """Form to start a new thread.""" title = StrippedCharField(min_length=5, max_length=255, label=_lazy('Title:'), widget=forms.TextInput(attrs={'size': 80}), error_messages={'required': MSG_TITLE_REQUIRED, 'min_length': MSG_TITLE_SHORT, 'max_length': MSG_TITLE_LONG}) content = StrippedCharField( label=_lazy('Content:'), min_length=5, max_length=10000, widget=forms.Textarea(attrs={'rows': 30, 'cols': 76}), error_messages={'required': MSG_CONTENT_REQUIRED, 'min_length': MSG_CONTENT_SHORT, 'max_length': MSG_CONTENT_LONG})
class ReviewForm(forms.Form): comment = StrippedCharField(max_length=2000, widget=forms.Textarea(), required=False, label=_lazy(u'Comment:'), error_messages={'max_length': COMMENT_LONG}) _widget = forms.RadioSelect(renderer=RadioFieldRendererWithHelpText) significance = forms.ChoiceField( label=_lazy(u'Significance:'), choices=SIGNIFICANCES, initial=SIGNIFICANCES[1][0], required=False, widget=_widget) is_ready_for_localization = forms.BooleanField( initial=False, label=_lazy(u'Ready for localization'), required=False) needs_change = forms.BooleanField( label=_lazy(u'Needs change'), initial=False, required=False) needs_change_comment = forms.CharField( label=_lazy(u'Comment:'), widget=forms.Textarea(), required=False)
class ReviewForm(forms.Form): comment = StrippedCharField(max_length=255, widget=forms.Textarea(), required=False, label=_lazy(u'Comment:'), error_messages={'max_length': COMMENT_LONG}) significance = forms.ChoiceField( label=_lazy(u'Significance:'), choices=SIGNIFICANCES, initial=SIGNIFICANCES[0][0], required=False, widget=forms.RadioSelect())
class EditThreadForm(forms.ModelForm): """Form to start a new thread.""" title = StrippedCharField(min_length=5, max_length=255, label=_lazy('Title:'), widget=forms.TextInput(attrs={'size': 80}), error_messages={'required': MSG_TITLE_REQUIRED, 'min_length': MSG_TITLE_SHORT, 'max_length': MSG_TITLE_LONG}) class Meta: model = Thread fields = ('title',)
class ReplyForm(forms.ModelForm): """Reply form for forum threads.""" content = StrippedCharField( label=_lazy('Content:'), min_length=5, max_length=10000, widget=forms.Textarea(attrs={'rows': 10, 'cols': 80}), error_messages={'required': MSG_CONTENT_REQUIRED, 'min_length': MSG_CONTENT_SHORT, 'max_length': MSG_CONTENT_LONG}) class Meta: model = Post fields = ('content', )
class TreeMoveForm(forms.Form): title = StrippedCharField( min_length=1, max_length=255, required=False, widget=forms.TextInput(attrs={'placeholder': TITLE_PLACEHOLDER}), label=_lazy(u'Title:'), help_text=_lazy(u'Title of article'), error_messages={ 'required': TITLE_REQUIRED, 'min_length': TITLE_SHORT, 'max_length': TITLE_LONG }) slug = StrippedCharField(min_length=1, max_length=255, widget=forms.TextInput(), label=_lazy(u'New slug:'), help_text=_lazy(u'New article URL'), error_messages={ 'required': SLUG_REQUIRED, 'min_length': SLUG_SHORT, 'max_length': SLUG_LONG })
class EditPostForm(forms.Form): """Form to edit an existing post.""" content = StrippedCharField( label=_lazy('Content:'), min_length=5, max_length=10000, widget=forms.Textarea(attrs={'rows': 30, 'cols': 76}), error_messages={'required': MSG_CONTENT_REQUIRED, 'min_length': MSG_CONTENT_SHORT, 'max_length': MSG_CONTENT_LONG}) class Meta: model = Post exclude = ('thread', 'author', 'updated', 'created', 'updated_by')
class MediaForm(forms.ModelForm): """Common abstractions for Image and Video forms.""" locale = forms.ChoiceField(required=False, label=_lazy(u'Locale'), choices=[(k, LOCALES[k].native) for k in settings.SUMO_LANGUAGES], initial=settings.WIKI_DEFAULT_LANGUAGE) title = StrippedCharField(required=False, label=_lazy(u'Title'), help_text=TITLE_HELP_TEXT % {'type': u'Image'}, min_length=5, max_length=255, error_messages={ 'required': MSG_TITLE_REQUIRED, 'min_length': MSG_TITLE_SHORT, 'max_length': MSG_TITLE_LONG }) description = StrippedCharField(required=False, label=_lazy(u'Description'), help_text=DESCRIPTION_HELP_TEXT, max_length=10000, widget=forms.Textarea(), error_messages={ 'required': MSG_DESCRIPTION_REQUIRED, 'max_length': MSG_DESCRIPTION_LONG }) def __init__(self, *args, **kwargs): self.is_ajax = kwargs.pop('is_ajax', True) super(MediaForm, self).__init__(*args, **kwargs) if not self.is_ajax: self.fields['locale'].required = True self.fields['title'].required = True self.fields['description'].required = True def save(self, update_user=None, is_draft=True, **kwargs): return save_form(self, update_user, is_draft=is_draft, **kwargs)
class MarketplaceAaqForm(forms.Form): """AAQ Form for Marketplace.""" def __init__(self, user, *args, **kwargs): super(MarketplaceAaqForm, self).__init__(*args, **kwargs) # Add email field for users not logged in. if not user.is_authenticated(): email = forms.EmailField( label=_lazy(u'Email:'), widget=forms.TextInput( attrs={'placeholder': EMAIL_PLACEHOLDER})) self.fields['email'] = email subject = StrippedCharField( label=_lazy(u'Subject:'), min_length=4, max_length=255, widget=forms.TextInput(attrs={'placeholder': SUBJECT_PLACEHOLDER}), error_messages={ 'required': SUBJECT_CONTENT_REQUIRED, 'min_length': SUBJECT_CONTENT_SHORT, 'max_length': SUBJECT_CONTENT_LONG }) body = StrippedCharField( label=_lazy(u'Body:'), min_length=5, max_length=10000, widget=forms.Textarea(attrs={'placeholder': BODY_PLACEHOLDER}), error_messages={ 'required': BODY_CONTENT_REQUIRED, 'min_length': BODY_CONTENT_SHORT, 'max_length': BODY_CONTENT_LONG }) category = forms.ChoiceField(label=_lazy(u'Category:'), choices=CATEGORY_CHOICES)
class AnswerForm(forms.Form): """Form for replying to a question.""" content = StrippedCharField( label=_lazy('Content:'), min_length=5, max_length=10000, widget=forms.Textarea(attrs={'placeholder': REPLY_PLACEHOLDER}), error_messages={ 'required': MSG_CONTENT_REQUIRED, 'min_length': MSG_CONTENT_SHORT, 'max_length': MSG_CONTENT_LONG }) class Meta: model = Answer fields = ('content', )
class ReplyForm(forms.ModelForm): """Reply form for forum threads.""" content = StrippedCharField(min_length=5, max_length=10000, widget=forms.Textarea(attrs={ 'rows': 10, 'cols': 80 }), error_messages={ 'required': MSG_CONTENT_REQUIRED, 'min_length': MSG_CONTENT_SHORT, 'max_length': MSG_CONTENT_LONG }) class Meta: model = Post exclude = ('thread', 'author', 'created', 'updated', 'updated_by')
class RevisionForm(forms.ModelForm): """Form to create new revisions.""" title = StrippedCharField( min_length=1, max_length=255, required=False, widget=forms.TextInput(attrs={'placeholder': TITLE_PLACEHOLDER}), label=_lazy(u'Title:'), help_text=_lazy(u'Title of article'), error_messages={ 'required': TITLE_REQUIRED, 'min_length': TITLE_SHORT, 'max_length': TITLE_LONG }) slug = StrippedCharField(min_length=1, max_length=255, required=False, widget=forms.TextInput(), label=_lazy(u'Slug:'), help_text=_lazy(u'Article URL'), error_messages={ 'required': SLUG_REQUIRED, 'min_length': SLUG_SHORT, 'max_length': SLUG_LONG }) tags = StrippedCharField(required=False, label=_lazy(u'Tags:')) keywords = StrippedCharField(required=False, label=_lazy(u'Keywords:'), help_text=_lazy(u'Affects search results')) summary = StrippedCharField( required=False, min_length=5, max_length=1000, widget=forms.Textarea(), label=_lazy(u'Search result summary:'), help_text=_lazy(u'Only displayed on search results page'), error_messages={ 'required': SUMMARY_REQUIRED, 'min_length': SUMMARY_SHORT, 'max_length': SUMMARY_LONG }) content = StrippedCharField(min_length=5, max_length=300000, label=_lazy(u'Content:'), widget=forms.Textarea(), error_messages={ 'required': CONTENT_REQUIRED, 'min_length': CONTENT_SHORT, 'max_length': CONTENT_LONG }) comment = StrippedCharField(required=False, label=_lazy(u'Comment:')) review_tags = forms.MultipleChoiceField( label=_("Tag this revision for review?"), widget=CheckboxSelectMultiple, required=False, choices=REVIEW_FLAG_TAGS) localization_tags = forms.MultipleChoiceField( label=_("Tag this revision for localization?"), widget=CheckboxSelectMultiple, required=False, choices=LOCALIZATION_FLAG_TAGS) current_rev = forms.CharField(required=False, widget=forms.HiddenInput()) class Meta(object): model = Revision fields = ('title', 'slug', 'tags', 'keywords', 'summary', 'content', 'comment', 'based_on', 'toc_depth', 'render_max_age') def __init__(self, *args, **kwargs): # Snag some optional kwargs and delete them before calling # super-constructor. for n in ('section_id', 'is_iframe_target'): if n not in kwargs: setattr(self, n, None) else: setattr(self, n, kwargs[n]) del kwargs[n] super(RevisionForm, self).__init__(*args, **kwargs) self.fields['based_on'].widget = forms.HiddenInput() if self.instance and self.instance.pk: # Ensure both title and slug are populated from parent document, if # last revision didn't have them if not self.instance.title: self.initial['title'] = self.instance.document.title if not self.instance.slug: self.initial['slug'] = self.instance.document.slug content = self.instance.content if not self.instance.document.is_template: tool = wiki.content.parse(content) tool.injectSectionIDs() if self.section_id: tool.extractSection(self.section_id) tool.filterEditorSafety() content = tool.serialize() self.initial['content'] = content self.initial['review_tags'] = [ x.name for x in self.instance.review_tags.all() ] self.initial['localization_tags'] = [ x.name for x in self.instance.localization_tags.all() ] if self.section_id: self.fields['toc_depth'].required = False def _clean_collidable(self, name): value = self.cleaned_data[name] if self.is_iframe_target: # Since these collidables can change the URL of the page, changes # to them are ignored for an iframe submission return getattr(self.instance.document, name) error_message = {'slug': SLUG_COLLIDES}.get(name, OTHER_COLLIDES) try: existing_doc = Document.objects.get( locale=self.instance.document.locale, **{name: value}) if self.instance and self.instance.document: if (not existing_doc.redirect_url() and existing_doc.pk != self.instance.document.pk): # There's another document with this value, # and we're not a revision of it. raise forms.ValidationError(error_message) else: # This document-and-revision doesn't exist yet, so there # shouldn't be any collisions at all. raise forms.ValidationError(error_message) except Document.DoesNotExist: # No existing document for this value, so we're good here. pass return value def clean_slug(self): # TODO: move this check somewhere else? # edits can come in without a slug, so default to the current doc slug if not self.cleaned_data['slug']: existing_slug = self.instance.document.slug self.cleaned_data['slug'] = self.instance.slug = existing_slug cleaned_slug = self._clean_collidable('slug') return cleaned_slug def clean_content(self): """Validate the content, performing any section editing if necessary""" content = self.cleaned_data['content'] # If we're editing a section, we need to replace the section content # from the current revision. if self.section_id and self.instance and self.instance.document: # Make sure we start with content form the latest revision. full_content = self.instance.document.current_revision.content # Replace the section content with the form content. tool = wiki.content.parse(full_content) tool.replaceSection(self.section_id, content) content = tool.serialize() return content def clean_current_rev(self): """If a current revision is supplied in the form, compare it against what the document claims is the current revision. If there's a difference, then an edit has occurred since the form was constructed and we treat it as a mid-air collision.""" current_rev = self.cleaned_data.get('current_rev', None) if not current_rev: # If there's no current_rev, just bail. return current_rev try: doc_current_rev = self.instance.document.current_revision.id if unicode(current_rev) != unicode(doc_current_rev): if (self.section_id and self.instance and self.instance.document): # This is a section edit. So, even though the revision has # changed, it still might not be a collision if the section # in particular hasn't changed. orig_ct = (Revision.objects.get( pk=current_rev).get_section_content(self.section_id)) curr_ct = (self.instance.document.current_revision. get_section_content(self.section_id)) if orig_ct != curr_ct: # Oops. Looks like the section did actually get # changed, so yeah this is a collision. raise forms.ValidationError(MIDAIR_COLLISION) return current_rev else: # No section edit, so this is a flat-out collision. raise forms.ValidationError(MIDAIR_COLLISION) except Document.DoesNotExist: # If there's no document yet, just bail. return current_rev def save_section(self, creator, document, **kwargs): """Save a section edit.""" # This is separate because the logic is slightly different and # may need to evolve over time; a section edit doesn't submit # all the fields, and we need to account for that when we # construct the new Revision. old_rev = Document.objects.get( pk=self.instance.document.id).current_revision new_rev = super(RevisionForm, self).save(commit=False, **kwargs) new_rev.document = document new_rev.creator = creator new_rev.toc_depth = old_rev.toc_depth new_rev.save() new_rev.review_tags.set(*[t.name for t in old_rev.review_tags.all()]) return new_rev def save(self, creator, document, **kwargs): """Persist me, and return the saved Revision. Take several other necessary pieces of data that aren't from the form. """ if self.section_id and self.instance and \ self.instance.document: return self.save_section(creator, document, **kwargs) # Throws a TypeError if somebody passes in a commit kwarg: new_rev = super(RevisionForm, self).save(commit=False, **kwargs) new_rev.document = document new_rev.creator = creator new_rev.toc_depth = self.cleaned_data['toc_depth'] new_rev.save() new_rev.review_tags.set(*self.cleaned_data['review_tags']) new_rev.localization_tags.set(*self.cleaned_data['localization_tags']) return new_rev
class DocumentForm(forms.ModelForm): """Form to create/edit a document.""" title = StrippedCharField( min_length=1, max_length=255, widget=forms.TextInput(attrs={'placeholder': TITLE_PLACEHOLDER}), label=_lazy(u'Title:'), help_text=_lazy(u'Title of article'), error_messages={ 'required': TITLE_REQUIRED, 'min_length': TITLE_SHORT, 'max_length': TITLE_LONG }) slug = StrippedCharField(min_length=1, max_length=255, widget=forms.TextInput(), label=_lazy(u'Slug:'), help_text=_lazy(u'Article URL'), error_messages={ 'required': SLUG_REQUIRED, 'min_length': SLUG_SHORT, 'max_length': SLUG_LONG }) firefox_versions = forms.MultipleChoiceField( label=_lazy(u'Firefox version:'), choices=[(v.id, v.long) for v in FIREFOX_VERSIONS], initial=[v.id for v in GROUPED_FIREFOX_VERSIONS[0][1]], required=False, widget=forms.CheckboxSelectMultiple()) operating_systems = forms.MultipleChoiceField( label=_lazy(u'Operating systems:'), choices=[(o.id, o.name) for o in OPERATING_SYSTEMS], initial=[o.id for o in GROUPED_OPERATING_SYSTEMS[0][1]], required=False, widget=forms.CheckboxSelectMultiple()) category = forms.ChoiceField( choices=CATEGORIES, initial=10, # Required for non-translations, which is # enforced in Document.clean(). required=False, label=_lazy(u'Category:'), help_text=_lazy(u'Type of article'), widget=forms.HiddenInput()) parent_topic = forms.ModelChoiceField(queryset=Document.objects.all(), required=False, label=_lazy(u'Parent:')) locale = forms.CharField(widget=forms.HiddenInput()) def clean_slug(self): slug = self.cleaned_data['slug'] if slug == '': # Default to the title, if missing. slug = self.cleaned_data['title'] # "?" disallowed in slugs altogether if '?' in slug: raise forms.ValidationError(SLUG_INVALID) # Pattern copied from urls.py if not re.compile(r'^[^\$]+$').match(slug): raise forms.ValidationError(SLUG_INVALID) # Guard against slugs that match urlpatterns for pat in RESERVED_SLUGS: if re.compile(pat).match(slug): raise forms.ValidationError(SLUG_INVALID) return slug def clean_firefox_versions(self): data = self.cleaned_data['firefox_versions'] return [FirefoxVersion(item_id=int(x)) for x in data] def clean_operating_systems(self): data = self.cleaned_data['operating_systems'] return [OperatingSystem(item_id=int(x)) for x in data] class Meta: model = Document fields = ('title', 'slug', 'category', 'locale') def save(self, parent_doc, **kwargs): """Persist the Document form, and return the saved Document.""" doc = super(DocumentForm, self).save(commit=False, **kwargs) doc.parent = parent_doc if 'parent_topic' in self.cleaned_data: doc.parent_topic = self.cleaned_data['parent_topic'] doc.save() self.save_m2m() # not strictly necessary since we didn't change # any m2m data since we instantiated the doc if not parent_doc: ffv = self.cleaned_data['firefox_versions'] doc.firefox_versions.all().delete() doc.firefox_versions = ffv os = self.cleaned_data['operating_systems'] doc.operating_systems.all().delete() doc.operating_systems = os return doc
def __init__(self, product=None, category=None, *args, **kwargs): """Init the form. We are adding fields here and not declaratively because the form fields to include depend on the selected product/category. """ super(EditQuestionForm, self).__init__(*args, **kwargs) # Extra fields required by product/category selected extra_fields = [] if product: extra_fields += product.get('extra_fields', []) if category: extra_fields += category.get('extra_fields', []) # Add the fields to the form error_messages = { 'required': MSG_TITLE_REQUIRED, 'min_length': MSG_TITLE_SHORT, 'max_length': MSG_TITLE_LONG } field = StrippedCharField(label=TITLE_LABEL, min_length=5, max_length=255, widget=forms.TextInput(), error_messages=error_messages) self.fields['title'] = field error_messages = { 'required': MSG_CONTENT_REQUIRED, 'min_length': MSG_CONTENT_SHORT, 'max_length': MSG_CONTENT_LONG } field = StrippedCharField(label=CONTENT_LABEL, help_text=CONTENT_HELP, min_length=5, max_length=10000, widget=forms.Textarea(), error_messages=error_messages) self.fields['content'] = field if 'sites_affected' in extra_fields: field = StrippedCharField(label=SITE_AFFECTED_LABEL, initial='http://', required=False, max_length=255, widget=forms.TextInput()) self.fields['sites_affected'] = field if 'crash_id' in extra_fields: field = StrippedCharField(label=CRASH_ID_LABEL, help_text=CRASH_ID_HELP, required=False, max_length=255, widget=forms.TextInput()) self.fields['crash_id'] = field if 'frequency' in extra_fields: field = forms.ChoiceField(label=FREQUENCY_LABEL, choices=FREQUENCY_CHOICES, required=False) self.fields['frequency'] = field if 'started' in extra_fields: field = StrippedCharField(label=STARTED_LABEL, required=False, max_length=255, widget=forms.TextInput()) self.fields['started'] = field if 'addon' in extra_fields: field = StrippedCharField(label=ADDON_LABEL, required=False, max_length=255, widget=forms.TextInput()) self.fields['addon'] = field if 'troubleshooting' in extra_fields: widget = forms.Textarea(attrs={'class': 'troubleshooting'}) field = StrippedCharField(label=TROUBLESHOOTING_LABEL, help_text=TROUBLESHOOTING_HELP, required=False, max_length=30000, widget=widget) self.fields['troubleshooting'] = field if 'ff_version' in extra_fields: field = StrippedCharField(label=FF_VERSION_LABEL, required=False) self.fields['ff_version'] = field if 'os' in extra_fields: self.fields['os'] = StrippedCharField(label=OS_LABEL, required=False) if 'plugins' in extra_fields: widget = forms.Textarea(attrs={'class': 'plugins'}) self.fields['plugins'] = StrippedCharField(label=PLUGINS_LABEL, required=False, widget=widget)
class RevisionForm(forms.ModelForm): """Form to create new revisions.""" keywords = StrippedCharField(required=False, label=_lazy(u'Keywords:'), help_text=_lazy(u'Affects search results')) summary = StrippedCharField( min_length=5, max_length=1000, widget=forms.Textarea(), label=_lazy(u'Search result summary:'), help_text=_lazy(u'Only displayed on search results page'), error_messages={'required': SUMMARY_REQUIRED, 'min_length': SUMMARY_SHORT, 'max_length': SUMMARY_LONG}) showfor_data = { 'oses': [(smart_str(c[0][0]), [(o.slug, smart_str(o.name)) for o in c[1]]) for c in GROUPED_OPERATING_SYSTEMS], 'versions': [(smart_str(c[0][0]), [(v.slug, smart_str(v.name)) for v in c[1] if v.show_in_ui]) for c in GROUPED_FIREFOX_VERSIONS]} content = StrippedCharField( min_length=5, max_length=100000, label=_lazy(u'Content:'), widget=forms.Textarea(attrs={'data-showfor': json.dumps(showfor_data)}), error_messages={'required': CONTENT_REQUIRED, 'min_length': CONTENT_SHORT, 'max_length': CONTENT_LONG}) comment = StrippedCharField(required=False, label=_lazy(u'Comment:')) class Meta(object): model = Revision fields = ('keywords', 'summary', 'content', 'comment', 'based_on') def __init__(self, *args, **kwargs): super(RevisionForm, self).__init__(*args, **kwargs) self.fields['based_on'].widget = forms.HiddenInput() self.fields['comment'].widget = forms.TextInput( attrs={'maxlength': MAX_REVISION_COMMENT_LENGTH}) def save(self, creator, document, based_on_id=None, base_rev=None, **kwargs): """Persist me, and return the saved Revision. Take several other necessary pieces of data that aren't from the form. """ # Throws a TypeError if somebody passes in a commit kwarg: new_rev = super(RevisionForm, self).save(commit=False, **kwargs) new_rev.document = document new_rev.creator = creator if based_on_id: new_rev.based_on_id = based_on_id # If the user doesn't have edit keywords permission, don't change. if base_rev and not creator.has_perm('wiki.edit_keywords'): new_rev.keywords = base_rev.keywords new_rev.save() return new_rev
class DocumentForm(forms.ModelForm): """Form to create/edit a document.""" def __init__(self, *args, **kwargs): # Quasi-kwargs: can_archive = kwargs.pop('can_archive', False) initial_title = kwargs.pop('initial_title', '') super(DocumentForm, self).__init__(*args, **kwargs) title_field = self.fields['title'] title_field.initial = initial_title slug_field = self.fields['slug'] slug_field.initial = slugify(initial_title) topics_field = self.fields['topics'] topics_field.choices = Topic.objects.values_list('id', 'title') products_field = self.fields['products'] products_field.choices = Product.objects.values_list('id', 'title') # If user hasn't permission to frob is_archived, remove the field. This # causes save() to skip it as well. if not can_archive: del self.fields['is_archived'] title = StrippedCharField( min_length=5, max_length=255, widget=forms.TextInput(), label=_lazy(u'Title:'), help_text=_lazy(u'Title of article'), error_messages={'required': TITLE_REQUIRED, 'min_length': TITLE_SHORT, 'max_length': TITLE_LONG}) # We don't use forms.SlugField because it is too strict in # what it allows (English/Roman alpha-numeric characters and dashes). # Instead, we do custom validation in `clean_slug` below. slug = StrippedCharField( min_length=3, max_length=255, widget=forms.TextInput(), label=_lazy(u'Slug:'), help_text=_lazy(u'Article URL'), error_messages={'required': SLUG_REQUIRED, 'min_length': SLUG_SHORT, 'max_length': SLUG_LONG}) products = forms.MultipleChoiceField( label=_lazy(u'Relevant to:'), required=False, widget=forms.CheckboxSelectMultiple()) is_localizable = forms.BooleanField( initial=True, label=_lazy(u'Allow translations:'), required=False) is_archived = forms.BooleanField( label=_lazy(u'Obsolete:'), required=False) allow_discussion = forms.BooleanField( label=_lazy(u'Allow discussion on this article?'), initial=True, required=False) category = forms.ChoiceField( choices=CATEGORIES, # Required for non-translations, which is # enforced in Document.clean(). required=False, label=_lazy(u'Category:'), help_text=_lazy(u'Type of article')) topics = forms.MultipleChoiceField( label=_lazy(u'Topics:'), required=False, widget=forms.CheckboxSelectMultiple()) locale = forms.CharField(widget=forms.HiddenInput()) needs_change = forms.BooleanField( label=_lazy(u'Needs change:'), initial=False, required=False) needs_change_comment = forms.CharField( label=_lazy(u'Comment:'), widget=forms.Textarea(), required=False) def clean_slug(self): slug = self.cleaned_data['slug'] # Blacklist /, ? and + if not re.compile(r'^[^/^\+^\?]+$').match(slug): raise forms.ValidationError(SLUG_INVALID) return slug def clean(self): c = super(DocumentForm, self).clean() locale = c.get('locale') # Products are required for en-US products = c.get('products') if (locale == settings.WIKI_DEFAULT_LANGUAGE and (not products or len(products) < 1)): raise forms.ValidationError(PRODUCT_REQUIRED) # Topics are required for en-US topics = c.get('topics') if (locale == settings.WIKI_DEFAULT_LANGUAGE and (not topics or len(topics) < 1)): raise forms.ValidationError(TOPIC_REQUIRED) return c class Meta: model = Document fields = ('title', 'slug', 'category', 'is_localizable', 'products', 'topics', 'locale', 'is_archived', 'allow_discussion', 'needs_change', 'needs_change_comment') def save(self, parent_doc, **kwargs): """Persist the Document form, and return the saved Document.""" doc = super(DocumentForm, self).save(commit=False, **kwargs) doc.parent = parent_doc # If document doesn't need change, clear out the comment. if not doc.needs_change: doc.needs_change_comment = '' doc.save() self.save_m2m() if parent_doc: # Products are not set on translations. doc.products.remove(*[p for p in doc.products.all()]) return doc
class DocumentForm(forms.ModelForm): """Form to create/edit a document.""" title = StrippedCharField( min_length=1, max_length=255, widget=forms.TextInput(attrs={'placeholder': TITLE_PLACEHOLDER}), label=_lazy(u'Title:'), help_text=_lazy(u'Title of article'), error_messages={ 'required': TITLE_REQUIRED, 'min_length': TITLE_SHORT, 'max_length': TITLE_LONG }) slug = StrippedCharField(min_length=1, max_length=255, widget=forms.TextInput(), label=_lazy(u'Slug:'), help_text=_lazy(u'Article URL'), error_messages={ 'required': SLUG_REQUIRED, 'min_length': SLUG_SHORT, 'max_length': SLUG_LONG }) category = forms.ChoiceField( choices=Document.CATEGORIES, initial=10, # Required for non-translations, which is # enforced in Document.clean(). required=False, label=_lazy(u'Category:'), help_text=_lazy(u'Type of article'), widget=forms.HiddenInput()) parent_topic = forms.ModelChoiceField(queryset=Document.objects.all(), required=False, label=_lazy(u'Parent:')) locale = forms.CharField(widget=forms.HiddenInput()) def clean_slug(self): slug = self.cleaned_data['slug'] if slug == '': # Default to the title, if missing. slug = self.cleaned_data['title'] # "?", " ", quote disallowed in slugs altogether if '?' in slug or ' ' in slug or '"' in slug or "'" in slug: raise forms.ValidationError(SLUG_INVALID) # Pattern copied from urls.py if not re.compile(r'^[^\$]+$').match(slug): raise forms.ValidationError(SLUG_INVALID) # Guard against slugs that match urlpatterns for pat in RESERVED_SLUGS: if re.compile(pat).match(slug): raise forms.ValidationError(SLUG_INVALID) return slug class Meta: model = Document fields = ('title', 'slug', 'category', 'locale') def save(self, parent_doc, **kwargs): """Persist the Document form, and return the saved Document.""" doc = super(DocumentForm, self).save(commit=False, **kwargs) doc.parent = parent_doc if 'parent_topic' in self.cleaned_data: doc.parent_topic = self.cleaned_data['parent_topic'] doc.save() # not strictly necessary since we didn't change # any m2m data since we instantiated the doc self.save_m2m() return doc
class DocumentForm(forms.ModelForm): """Form to create/edit a document.""" def __init__(self, *args, **kwargs): # Quasi-kwargs: can_create_tags = kwargs.pop('can_create_tags', False) can_archive = kwargs.pop('can_archive', False) super(DocumentForm, self).__init__(*args, **kwargs) # Set up tags field, which is instantiated deep within taggit: tags_field = self.fields['tags'] tags_field.widget.can_create_tags = can_create_tags # If user hasn't permission to frob is_archived, remove the field. This # causes save() to skip it as well. if not can_archive: del self.fields['is_archived'] title = StrippedCharField(min_length=5, max_length=255, widget=forms.TextInput(), label=_lazy(u'Title:'), help_text=_lazy(u'Title of article'), error_messages={ 'required': TITLE_REQUIRED, 'min_length': TITLE_SHORT, 'max_length': TITLE_LONG }) # We don't use forms.SlugField because it is too strict in # what it allows (English/Roman alpha-numeric characters and dashes). # Instead, we do custom validation in `clean_slug` below. slug = StrippedCharField(min_length=3, max_length=255, widget=forms.TextInput(), label=_lazy(u'Slug:'), help_text=_lazy(u'Article URL'), error_messages={ 'required': SLUG_REQUIRED, 'min_length': SLUG_SHORT, 'max_length': SLUG_LONG }) products = forms.MultipleChoiceField(label=_lazy(u'Relevant to:'), choices=PRODUCTS, initial=[PRODUCTS[0][0]], required=False, widget=forms.CheckboxSelectMultiple()) is_localizable = forms.BooleanField(initial=True, label=_lazy(u'Allow translations:'), required=False) is_archived = forms.BooleanField(label=_lazy(u'Obsolete:'), required=False) allow_discussion = forms.BooleanField( label=_lazy(u'Allow discussion on this article?'), initial=True, required=False) category = forms.ChoiceField( choices=CATEGORIES, # Required for non-translations, which is # enforced in Document.clean(). required=False, label=_lazy(u'Category:'), help_text=_lazy(u'Type of article')) tags = tag_forms.TagField( required=False, label=_lazy(u'Topics:'), help_text=_lazy(u'Popular articles in each topic are displayed on the ' 'front page')) locale = forms.CharField(widget=forms.HiddenInput()) needs_change = forms.BooleanField(label=_lazy(u'Needs change:'), initial=False, required=False) needs_change_comment = forms.CharField(label=_lazy(u'Comment:'), widget=forms.Textarea(), required=False) def clean_slug(self): slug = self.cleaned_data['slug'] # Blacklist /, ? and + if not re.compile(r'^[^/^\+^\?]+$').match(slug): raise forms.ValidationError(SLUG_INVALID) return slug class Meta: model = Document fields = ('title', 'slug', 'category', 'is_localizable', 'products', 'tags', 'locale', 'is_archived', 'allow_discussion', 'needs_change', 'needs_change_comment') def save(self, parent_doc, **kwargs): """Persist the Document form, and return the saved Document.""" doc = super(DocumentForm, self).save(commit=False, **kwargs) doc.parent = parent_doc # If document doesn't need change, clear out the comment. if not doc.needs_change: doc.needs_change_comment = '' doc.save() self.save_m2m() # not strictly necessary since we didn't change # any m2m data since we instantiated the doc if not parent_doc: # Set the products as tags. # products are not set on the translations. prods = self.cleaned_data['products'] doc.tags.add(*prods) doc.tags.remove(*[p for p in PRODUCT_TAGS if p not in prods]) return doc