class AdvancedSettingsForm(forms.ModelForm): from cms.forms.fields import PageSmartLinkField _user = None _site = None _language = None application_urls = forms.ChoiceField( label=_('Application'), choices=(), required=False, help_text=_('Hook application to this page.')) overwrite_url = forms.CharField( label=_('Overwrite URL'), max_length=255, required=False, help_text=_('Keep this field empty if standard path should be used.')) xframe_options = forms.ChoiceField( choices=Page._meta.get_field('xframe_options').choices, label=_('X Frame Options'), help_text=_( 'Whether this page can be embedded in other pages or websites'), initial=Page._meta.get_field('xframe_options').default, required=False) redirect = PageSmartLinkField( label=_('Redirect'), required=False, help_text=_('Redirects to this URL.'), placeholder_text=_('Start typing...'), ajax_view='admin:cms_page_get_published_pagelist') # This is really a 'fake' field which does not correspond to any Page attribute # But creates a stub field to be populate by js application_configs = forms.CharField( label=_('Application configurations'), required=False, widget=ApplicationConfigSelect, ) fieldsets = ((None, { 'fields': ('overwrite_url', 'redirect'), }), (_('Language independent options'), { 'fields': ( 'template', 'reverse_id', 'soft_root', 'navigation_extenders', 'application_urls', 'application_namespace', 'application_configs', 'xframe_options', ) })) class Meta: model = Page fields = [ 'template', 'reverse_id', 'overwrite_url', 'redirect', 'soft_root', 'navigation_extenders', 'application_urls', 'application_namespace', "xframe_options", ] def __init__(self, *args, **kwargs): super(AdvancedSettingsForm, self).__init__(*args, **kwargs) self.title_obj = self.instance.get_title_obj( language=self._language, fallback=False, force_reload=True, ) if 'navigation_extenders' in self.fields: navigation_extenders = self.get_navigation_extenders() self.fields['navigation_extenders'].widget = forms.Select( {}, [('', "---------")] + navigation_extenders) if 'application_urls' in self.fields: # Prepare a dict mapping the apps by class name ('PollApp') to # their app_name attribute ('polls'), if any. app_namespaces = {} app_configs = {} for hook in apphook_pool.get_apphooks(): app = apphook_pool.get_apphook(hook[0]) if app.app_name: app_namespaces[hook[0]] = app.app_name if app.app_config: app_configs[hook[0]] = app self.fields['application_urls'].widget = AppHookSelect( attrs={'id': 'application_urls'}, app_namespaces=app_namespaces) self.fields['application_urls'].choices = [ ('', "---------") ] + apphook_pool.get_apphooks() page_data = self.data if self.data else self.initial if app_configs: self.fields[ 'application_configs'].widget = ApplicationConfigSelect( attrs={'id': 'application_configs'}, app_configs=app_configs, ) if page_data.get( 'application_urls', False ) and page_data['application_urls'] in app_configs: configs = app_configs[ page_data['application_urls']].get_configs() self.fields['application_configs'].widget.choices = [ (config.pk, force_text(config)) for config in configs ] try: config = configs.get( namespace=self.initial['application_namespace']) self.fields['application_configs'].initial = config.pk except ObjectDoesNotExist: # Provided apphook configuration doesn't exist (anymore), # just skip it # The user will choose another value anyway pass if 'redirect' in self.fields: self.fields['redirect'].widget.language = self._language self.fields['redirect'].initial = self.title_obj.redirect if 'overwrite_url' in self.fields and self.title_obj.has_url_overwrite: self.fields['overwrite_url'].initial = self.title_obj.path def get_apphooks(self): for hook in apphook_pool.get_apphooks(): yield (hook[0], apphook_pool.get_apphook(hook[0])) def get_apphooks_with_config(self): return {key: app for key, app in self.get_apphooks() if app.app_config} def get_navigation_extenders(self): return menu_pool.get_menus_by_attribute("cms_enabled", True) def _check_unique_namespace_instance(self, namespace): return Page.objects.drafts().on_site( self._site).filter(application_namespace=namespace).exclude( pk=self.instance.pk).exists() def clean(self): cleaned_data = super(AdvancedSettingsForm, self).clean() if self._errors: # Fail fast if there's errors in the form return cleaned_data # Language has been validated already # so we know it exists. language_name = get_language_object( self._language, site_id=self._site.pk, )['name'] if not self.title_obj.slug: # This covers all cases where users try to edit # page advanced settings without setting a title slug # for page titles that already exist. message = _("Please set the %(language)s slug " "before editing its advanced settings.") raise ValidationError(message % {'language': language_name}) if 'reverse_id' in self.fields: reverse_id = cleaned_data['reverse_id'] if reverse_id: lookup = Page.objects.drafts().on_site( self._site).filter(reverse_id=reverse_id) if lookup.exclude(pk=self.instance.pk).exists(): self._errors['reverse_id'] = self.error_class( [_('A page with this reverse URL id exists already.')]) apphook = cleaned_data.get('application_urls', None) # The field 'application_namespace' is a misnomer. It should be # 'instance_namespace'. instance_namespace = cleaned_data.get('application_namespace', None) application_config = cleaned_data.get('application_configs', None) if apphook: apphooks_with_config = self.get_apphooks_with_config() # application_config wins over application_namespace if apphook in apphooks_with_config and application_config: # the value of the application config namespace is saved in # the 'usual' namespace field to be backward compatible # with existing apphooks try: appconfig_pk = forms.IntegerField( required=True).to_python(application_config) except ValidationError: self._errors['application_configs'] = ErrorList( [_('Invalid application config value')]) return self.cleaned_data try: config = apphooks_with_config[apphook].get_configs().get( pk=appconfig_pk) except ObjectDoesNotExist: self._errors['application_configs'] = ErrorList( [_('Invalid application config value')]) return self.cleaned_data if self._check_unique_namespace_instance(config.namespace): # Looks like there's already one with the default instance # namespace defined. self._errors['application_configs'] = ErrorList([ _('An application instance using this configuration already exists.' ) ]) else: self.cleaned_data[ 'application_namespace'] = config.namespace else: if instance_namespace: if self._check_unique_namespace_instance( instance_namespace): self._errors['application_namespace'] = ErrorList([ _('An application instance with this name already exists.' ) ]) else: # The attribute on the apps 'app_name' is a misnomer, it should be # 'application_namespace'. application_namespace = apphook_pool.get_apphook( apphook).app_name if application_namespace and not instance_namespace: if self._check_unique_namespace_instance( application_namespace): # Looks like there's already one with the default instance # namespace defined. self._errors['application_namespace'] = ErrorList([ _('An application instance with this name already exists.' ) ]) else: # OK, there are zero instances of THIS app that use the # default instance namespace, so, since the user didn't # provide one, we'll use the default. NOTE: The following # line is really setting the "instance namespace" of the # new app to the app’s "application namespace", which is # the default instance namespace. self.cleaned_data[ 'application_namespace'] = application_namespace if instance_namespace and not apphook: self.cleaned_data['application_namespace'] = None if application_config and not apphook: self.cleaned_data['application_configs'] = None return self.cleaned_data def clean_xframe_options(self): if 'xframe_options' not in self.fields: return # nothing to do, field isn't present xframe_options = self.cleaned_data['xframe_options'] if xframe_options == '': return Page._meta.get_field('xframe_options').default return xframe_options def clean_overwrite_url(self): path_override = self.cleaned_data.get('overwrite_url') if path_override: path = path_override.strip('/') else: path = self.instance.get_path_for_slug(self.title_obj.slug, self._language) validate_url_uniqueness( self._site, path=path, language=self._language, exclude_page=self.instance, ) self.cleaned_data['path'] = path return path_override def has_changed_apphooks(self): changed_data = self.changed_data if 'application_urls' in changed_data: return True return 'application_namespace' in changed_data def update_apphooks(self): # User has changed the apphooks on the page. # Update the public version of the page to reflect this change immediately. public_id = self.instance.publisher_public_id self._meta.model.objects.filter(pk=public_id).update( application_urls=self.instance.application_urls, application_namespace=(self.instance.application_namespace or None), ) # Connects the apphook restart handler to the request finished signal set_restart_trigger() def save(self, *args, **kwargs): data = self.cleaned_data page = super(AdvancedSettingsForm, self).save(*args, **kwargs) page.update_translations( self._language, path=data['path'], redirect=(data.get('redirect') or None), publisher_state=PUBLISHER_STATE_DIRTY, has_url_overwrite=bool(data.get('overwrite_url')), ) is_draft_and_has_public = page.publisher_is_draft and page.publisher_public_id if is_draft_and_has_public and self.has_changed_apphooks(): self.update_apphooks() page.clear_cache(menu=True) return page
class AdvancedSettingsForm(forms.ModelForm): from cms.forms.fields import PageSmartLinkField application_urls = forms.ChoiceField(label=_('Application'), choices=(), required=False, help_text=_('Hook application to this page.')) overwrite_url = forms.CharField(label=_('Overwrite URL'), max_length=255, required=False, help_text=_('Keep this field empty if standard path should be used.')) xframe_options = forms.ChoiceField( choices=Page._meta.get_field('xframe_options').choices, label=_('X Frame Options'), help_text=_('Whether this page can be embedded in other pages or websites'), initial=Page._meta.get_field('xframe_options').default, required=False ) redirect = PageSmartLinkField(label=_('Redirect'), required=False, help_text=_('Redirects to this URL.'), placeholder_text=_('Start typing...'), ajax_view='admin:cms_page_get_published_pagelist' ) language = forms.ChoiceField(label=_("Language"), choices=get_language_tuple(), help_text=_('The current language of the content fields.')) fieldsets = ( (None, { 'fields': ('overwrite_url','redirect'), }), ('Language independent options', { 'fields': ('site', 'template', 'reverse_id', 'soft_root', 'navigation_extenders', 'application_urls', 'application_namespace', "xframe_options",) }) ) def __init__(self, *args, **kwargs): super(AdvancedSettingsForm, self).__init__(*args, **kwargs) self.fields['language'].widget = HiddenInput() self.fields['site'].widget = HiddenInput() site_id = self.fields['site'].initial languages = get_language_tuple(site_id) self.fields['language'].choices = languages if not self.fields['language'].initial: self.fields['language'].initial = get_language() if 'navigation_extenders' in self.fields: self.fields['navigation_extenders'].widget = forms.Select({}, [('', "---------")] + menu_pool.get_menus_by_attribute("cms_enabled", True)) if 'application_urls' in self.fields: # Prepare a dict mapping the apps by class name ('PollApp') to # their app_name attribute ('polls'), if any. app_namespaces = {} for hook in apphook_pool.get_apphooks(): app = apphook_pool.get_apphook(hook[0]) if app.app_name: app_namespaces[hook[0]] = app.app_name self.fields['application_urls'].widget = AppHookSelect( attrs={'id':'application_urls'}, app_namespaces=app_namespaces, ) self.fields['application_urls'].choices = [('', "---------")] + apphook_pool.get_apphooks() if 'redirect' in self.fields: self.fields['redirect'].widget.language = self.fields['language'].initial def clean(self): cleaned_data = super(AdvancedSettingsForm, self).clean() if 'reverse_id' in self.fields: id = cleaned_data['reverse_id'] site_id = cleaned_data['site'] if id: if Page.objects.filter(reverse_id=id, site=site_id, publisher_is_draft=True).exclude( pk=self.instance.pk).count(): self._errors['reverse_id'] = self.error_class( [_('A page with this reverse URL id exists already.')]) apphook = cleaned_data.get('application_urls', None) # The field 'application_namespace' is a misnomer. It should be # 'instance_namespace'. instance_namespace = cleaned_data.get('application_namespace', None) if apphook: # The attribute on the apps 'app_name' is a misnomer, it should be # 'application_namespace'. application_namespace = apphook_pool.get_apphook(apphook).app_name if application_namespace and not instance_namespace: if Page.objects.filter( publisher_is_draft=True, application_urls=apphook, application_namespace=application_namespace ).exclude(pk=self.instance.pk).count(): # Looks like there's already one with the default instance # namespace defined. self._errors['application_urls'] = ErrorList([ _('''You selected an apphook with an "app_name". You must enter a unique instance name.''') ]) else: # OK, there are zero instances of THIS app that use the # default instance namespace, so, since the user didn't # provide one, we'll use the default. NOTE: The following # line is really setting the "instance namespace" of the # new app to the app’s "application namespace", which is # the default instance namespace. self.cleaned_data['application_namespace'] = application_namespace if instance_namespace and not apphook: self.cleaned_data['application_namespace'] = None return cleaned_data def clean_application_namespace(self): namespace = self.cleaned_data['application_namespace'] if namespace and Page.objects.filter(publisher_is_draft=True, application_namespace=namespace).exclude(pk=self.instance.pk).count(): raise ValidationError(_('A instance name with this name already exists.')) return namespace def clean_xframe_options(self): if 'xframe_options' not in self.fields: return # nothing to do, field isn't present xframe_options = self.cleaned_data['xframe_options'] if xframe_options == '': return Page._meta.get_field('xframe_options').default return xframe_options def clean_overwrite_url(self): if 'overwrite_url' in self.fields: url = self.cleaned_data['overwrite_url'] is_valid_url(url, self.instance) return url class Meta: model = Page fields = [ 'site', 'template', 'reverse_id', 'overwrite_url', 'redirect', 'soft_root', 'navigation_extenders', 'application_urls', 'application_namespace', "xframe_options", ]
class AdvancedSettingsForm(forms.ModelForm): from cms.forms.fields import PageSmartLinkField application_urls = forms.ChoiceField( label=_('Application'), choices=(), required=False, help_text=_('Hook application to this page.')) overwrite_url = forms.CharField( label=_('Overwrite URL'), max_length=255, required=False, help_text=_('Keep this field empty if standard path should be used.')) xframe_options = forms.ChoiceField( choices=Page._meta.get_field('xframe_options').choices, label=_('X Frame Options'), help_text=_( 'Whether this page can be embedded in other pages or websites'), initial=Page._meta.get_field('xframe_options').default, required=False) redirect = PageSmartLinkField( label=_('Redirect'), required=False, help_text=_('Redirects to this URL.'), placeholder_text=_('Start typing...'), ajax_view='admin:cms_page_get_published_pagelist') language = forms.ChoiceField( label=_("Language"), choices=get_language_tuple(), help_text=_('The current language of the content fields.')) # This is really a 'fake' field which does not correspond to any Page attribute # But creates a stub field to be populate by js application_configs = forms.ChoiceField( label=_('Application configurations'), choices=(), required=False, ) fieldsets = ((None, { 'fields': ('overwrite_url', 'redirect'), }), (_('Language independent options'), { 'fields': ( 'site', 'template', 'reverse_id', 'soft_root', 'navigation_extenders', 'application_urls', 'application_namespace', 'application_configs', 'xframe_options', ) })) def __init__(self, *args, **kwargs): super(AdvancedSettingsForm, self).__init__(*args, **kwargs) self.fields['language'].widget = HiddenInput() self.fields['site'].widget = HiddenInput() site_id = self.fields['site'].initial languages = get_language_tuple(site_id) self.fields['language'].choices = languages if not self.fields['language'].initial: self.fields['language'].initial = get_language() if 'navigation_extenders' in self.fields: navigation_extenders = self.get_navigation_extenders() self.fields['navigation_extenders'].widget = forms.Select( {}, [('', "---------")] + navigation_extenders) if 'application_urls' in self.fields: # Prepare a dict mapping the apps by class name ('PollApp') to # their app_name attribute ('polls'), if any. app_namespaces = {} app_configs = {} for hook in apphook_pool.get_apphooks(): app = apphook_pool.get_apphook(hook[0]) if app.app_name: app_namespaces[hook[0]] = app.app_name if app.app_config: app_configs[hook[0]] = app self.fields['application_urls'].widget = AppHookSelect( attrs={'id': 'application_urls'}, app_namespaces=app_namespaces) self.fields['application_urls'].choices = [ ('', "---------") ] + apphook_pool.get_apphooks() page_data = self.data if self.data else self.initial if app_configs: self.fields[ 'application_configs'].widget = ApplicationConfigSelect( attrs={'id': 'application_configs'}, app_configs=app_configs) if page_data.get( 'application_urls', False ) and page_data['application_urls'] in app_configs: self.fields['application_configs'].choices = [ (config.pk, force_text(config)) for config in app_configs[ page_data['application_urls']].get_configs() ] apphook = page_data.get('application_urls', False) try: config = apphook_pool.get_apphook(apphook).get_configs( ).get(namespace=self.initial['application_namespace']) self.fields['application_configs'].initial = config.pk except ObjectDoesNotExist: # Provided apphook configuration doesn't exist (anymore), # just skip it # The user will choose another value anyway pass else: # If app_config apphook is not selected, drop any value # for application_configs to avoid the field data from # being validated by the field itself try: del self.data['application_configs'] except KeyError: pass if 'redirect' in self.fields: self.fields['redirect'].widget.language = self.fields[ 'language'].initial def get_navigation_extenders(self): return menu_pool.get_menus_by_attribute("cms_enabled", True) def _check_unique_namespace_instance(self, namespace): return Page.objects.filter(publisher_is_draft=True, application_namespace=namespace).exclude( pk=self.instance.pk).exists() def clean(self): cleaned_data = super(AdvancedSettingsForm, self).clean() if self._errors: # Fail fast if there's errors in the form return cleaned_data language = cleaned_data['language'] # Language has been validated already # so we know it exists. language_name = get_language_object( language, site_id=cleaned_data['site'].pk)['name'] try: title = self.instance.title_set.get(language=language) except Title.DoesNotExist: # This covers all cases where users try to edit # page advanced settings without creating the page title. message = _("Please create the %(language)s page " "translation before editing its advanced settings.") raise ValidationError(message % {'language': language_name}) if not title.slug: # This covers all cases where users try to edit # page advanced settings without setting a title slug # for page titles that already exist. message = _("Please set the %(language)s slug " "before editing its advanced settings.") raise ValidationError(message % {'language': language_name}) if 'reverse_id' in self.fields: id = cleaned_data['reverse_id'] site_id = cleaned_data['site'] if id: if Page.objects.filter(reverse_id=id, site=site_id, publisher_is_draft=True).exclude( pk=self.instance.pk).exists(): self._errors['reverse_id'] = self.error_class( [_('A page with this reverse URL id exists already.')]) apphook = cleaned_data.get('application_urls', None) # The field 'application_namespace' is a misnomer. It should be # 'instance_namespace'. instance_namespace = cleaned_data.get('application_namespace', None) application_config = cleaned_data.get('application_configs', None) if apphook: # application_config wins over application_namespace if application_config: # the value of the application config namespace is saved in # the 'usual' namespace field to be backward compatible # with existing apphooks config = apphook_pool.get_apphook(apphook).get_configs().get( pk=int(application_config)) if self._check_unique_namespace_instance(config.namespace): # Looks like there's already one with the default instance # namespace defined. self._errors['application_configs'] = ErrorList([ _('An application instance using this configuration already exists.' ) ]) else: self.cleaned_data[ 'application_namespace'] = config.namespace else: if instance_namespace: if self._check_unique_namespace_instance( instance_namespace): self._errors['application_namespace'] = ErrorList([ _('An application instance with this name already exists.' ) ]) else: # The attribute on the apps 'app_name' is a misnomer, it should be # 'application_namespace'. application_namespace = apphook_pool.get_apphook( apphook).app_name if application_namespace and not instance_namespace: if self._check_unique_namespace_instance( application_namespace): # Looks like there's already one with the default instance # namespace defined. self._errors['application_namespace'] = ErrorList([ _('An application instance with this name already exists.' ) ]) else: # OK, there are zero instances of THIS app that use the # default instance namespace, so, since the user didn't # provide one, we'll use the default. NOTE: The following # line is really setting the "instance namespace" of the # new app to the app’s "application namespace", which is # the default instance namespace. self.cleaned_data[ 'application_namespace'] = application_namespace if instance_namespace and not apphook: self.cleaned_data['application_namespace'] = None if application_config and not apphook: self.cleaned_data['application_configs'] = None return self.cleaned_data def clean_xframe_options(self): if 'xframe_options' not in self.fields: return # nothing to do, field isn't present xframe_options = self.cleaned_data['xframe_options'] if xframe_options == '': return Page._meta.get_field('xframe_options').default return xframe_options def clean_overwrite_url(self): if 'overwrite_url' in self.fields: url = self.cleaned_data['overwrite_url'] is_valid_url(url, self.instance) return url class Meta: model = Page fields = [ 'site', 'template', 'reverse_id', 'overwrite_url', 'redirect', 'soft_root', 'navigation_extenders', 'application_urls', 'application_namespace', "xframe_options", ]
class AdvancedSettingsForm(forms.ModelForm): from cms.forms.fields import PageSmartLinkField application_urls = forms.ChoiceField( label=_('Application'), choices=(), required=False, help_text=_('Hook application to this page.')) overwrite_url = forms.CharField( label=_('Overwrite URL'), max_length=255, required=False, help_text=_('Keep this field empty if standard path should be used.')) xframe_options = forms.ChoiceField( choices=Page._meta.get_field('xframe_options').choices, label=_('X Frame Options'), help_text=_( 'Whether this page can be embedded in other pages or websites'), initial=Page._meta.get_field('xframe_options').default, required=False) redirect = PageSmartLinkField(label=_('Redirect'), required=False, help_text=_('Redirects to this URL.'), placeholder_text=_('Start typing...')) language = forms.ChoiceField( label=_("Language"), choices=get_language_tuple(), help_text=_('The current language of the content fields.')) def __init__(self, *args, **kwargs): super(AdvancedSettingsForm, self).__init__(*args, **kwargs) self.fields['language'].widget = HiddenInput() self.fields['site'].widget = HiddenInput() site_id = self.fields['site'].initial languages = get_language_tuple(site_id) self.fields['language'].choices = languages if not self.fields['language'].initial: self.fields['language'].initial = get_language() if 'navigation_extenders' in self.fields: self.fields['navigation_extenders'].widget = forms.Select( {}, [('', "---------")] + menu_pool.get_menus_by_attribute("cms_enabled", True)) if 'application_urls' in self.fields: self.fields['application_urls'].choices = [ ('', "---------") ] + apphook_pool.get_apphooks() if 'redirect' in self.fields: self.fields['redirect'].widget.language = self.fields[ 'language'].initial def clean(self): cleaned_data = super(AdvancedSettingsForm, self).clean() if 'reverse_id' in self.fields: id = cleaned_data['reverse_id'] site_id = cleaned_data['site'] if id: if Page.objects.filter(reverse_id=id, site=site_id, publisher_is_draft=True).exclude( pk=self.instance.pk).count(): self._errors['reverse_id'] = self.error_class( [_('A page with this reverse URL id exists already.')]) apphook = cleaned_data.get('application_urls', None) namespace = cleaned_data.get('application_namespace', None) if apphook: apphook_pool.discover_apps() if apphook_pool.apps[apphook].app_name and not namespace: self._errors['application_urls'] = ErrorList([ _('You selected an apphook with an "app_name". You must enter a instance name.' ) ]) if namespace and not apphook: self._errors['application_namespace'] = ErrorList([ _("If you enter an instance name you need an application url as well." ) ]) return cleaned_data def clean_application_namespace(self): namespace = self.cleaned_data['application_namespace'] if namespace and Page.objects.filter( publisher_is_draft=True, application_namespace=namespace).exclude( pk=self.instance.pk).count(): raise ValidationError( _('A instance name with this name already exists.')) return namespace def clean_xframe_options(self): if 'xframe_options' not in self.fields: return # nothing to do, field isn't present xframe_options = self.cleaned_data['xframe_options'] if xframe_options == '': return Page._meta.get_field('xframe_options').default return xframe_options def clean_overwrite_url(self): if 'overwrite_url' in self.fields: url = self.cleaned_data['overwrite_url'] is_valid_url(url, self.instance) return url class Meta: model = Page fields = [ 'site', 'template', 'reverse_id', 'overwrite_url', 'redirect', 'soft_root', 'navigation_extenders', 'application_urls', 'application_namespace', "xframe_options", ]