class PreferencesForm(forms.Form): redirect_to = forms.CharField(required=False, widget=forms.HiddenInput) groups = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, required=False) syntax_highlighting = forms.BooleanField( required=False, label=_("Enable syntax highlighting in the diff viewer")) profile_private = forms.BooleanField( required=False, label=_("Keep your user profile private")) open_an_issue = forms.BooleanField( required=False, label=_("Always open an issue when comment box opens")) first_name = forms.CharField(required=False) last_name = forms.CharField(required=False) email = forms.EmailField() password1 = forms.CharField(required=False, widget=widgets.PasswordInput()) password2 = forms.CharField(required=False, widget=widgets.PasswordInput()) timezone = TimeZoneField( label=_("Time Zone"), required=True, help_text=_("The time zone used for this account.")) def __init__(self, user, *args, **kwargs): from reviewboard.accounts.backends import get_auth_backends super(forms.Form, self).__init__(*args, **kwargs) auth_backends = get_auth_backends() choices = [] for g in Group.objects.accessible(user=user).order_by('display_name'): choices.append((g.id, g.display_name)) for site in user.local_site.all().order_by('name'): for g in Group.objects.accessible( user=user, local_site=site).order_by('display_name'): display_name = '%s / %s' % (g.local_site.name, g.display_name) choices.append((g.id, display_name)) self.fields['groups'].choices = choices self.fields['email'].required = auth_backends[0].supports_change_email def save(self, user): from reviewboard.accounts.backends import get_auth_backends auth_backends = get_auth_backends() primary_backend = auth_backends[0] password = self.cleaned_data['password1'] if primary_backend.supports_change_password and password: primary_backend.update_password(user, password) if primary_backend.supports_change_name: user.first_name = self.cleaned_data['first_name'] user.last_name = self.cleaned_data['last_name'] primary_backend.update_name(user) if primary_backend.supports_change_email: user.email = self.cleaned_data['email'] primary_backend.update_email(user) user.review_groups = self.cleaned_data['groups'] user.save() profile = user.get_profile() profile.first_time_setup_done = True profile.syntax_highlighting = self.cleaned_data['syntax_highlighting'] profile.is_private = self.cleaned_data['profile_private'] profile.open_an_issue = self.cleaned_data['open_an_issue'] profile.timezone = self.cleaned_data['timezone'] profile.save() def clean_password2(self): p1 = self.cleaned_data['password1'] p2 = self.cleaned_data['password2'] if p1 != p2: raise forms.ValidationError('passwords do not match') return p2
class GeneralSettingsForm(SiteSettingsForm): """General settings for Review Board.""" CACHE_TYPE_CHOICES = ( ('memcached', _('Memcached')), ('file', _('File cache')), ) CACHE_BACKENDS_MAP = { 'file': 'django.core.cache.backends.filebased.FileBasedCache', 'memcached': 'django.core.cache.backends.memcached.CacheClass', 'locmem': 'django.core.cache.backends.locmem.LocMemCache', } CACHE_TYPES_MAP = { 'django.core.cache.backends.filebased.FileBasedCache': 'file', 'django.core.cache.backends.memcached.CacheClass': 'memcached', 'django.core.cache.backends.locmem.LocMemCache': 'locmem', } CACHE_LOCATION_FIELD_MAP = { 'file': 'cache_path', 'memcached': 'cache_host', } server = forms.CharField( label=_("Server"), help_text=_("The URL of this Review Board server. This should not " "contain the subdirectory Review Board is installed in."), widget=forms.TextInput(attrs={'size': '30'})) site_media_url = forms.CharField( label=_("Media URL"), help_text=_("The URL to the media files. Leave blank to use the " "default media path on this server."), required=False, widget=forms.TextInput(attrs={'size': '30'})) site_admin_name = forms.CharField( label=_("Administrator Name"), required=True, widget=forms.TextInput(attrs={'size': '30'})) site_admin_email = forms.EmailField( label=_("Administrator E-Mail"), required=True, widget=forms.TextInput(attrs={'size': '30'})) locale_timezone = TimeZoneField( label=_("Time Zone"), required=True, help_text=_("The time zone used for all dates on this server.")) search_enable = forms.BooleanField( label=_("Enable search"), help_text=_("Provides a search field for quickly searching through " "review requests."), required=False) search_index_file = forms.CharField( label=_("Search index directory"), help_text=_("The directory that search index data should be stored " "in."), required=False, widget=forms.TextInput(attrs={'size': '50'})) cache_type = forms.ChoiceField( label=_("Cache Backend"), choices=CACHE_TYPE_CHOICES, help_text=_('The type of server-side caching to use.'), required=True) cache_path = forms.CharField( label=_("Cache Path"), help_text=_('The file location for the cache.'), required=True, widget=forms.TextInput(attrs={'size': '50'})) cache_host = forms.CharField( label=_("Cache Hosts"), help_text=_('The host or hosts used for the cache, in hostname:port ' 'form. Multiple hosts can be specified by separating ' 'them with a semicolon (;).'), required=True, widget=forms.TextInput(attrs={'size': '50'})) def load(self): domain_method = self.siteconfig.get("site_domain_method") site = Site.objects.get_current() can_enable_search, reason = get_can_enable_search() if not can_enable_search: self.disabled_fields['search_enable'] = True self.disabled_fields['search_index_file'] = True self.disabled_reasons['search_enable'] = reason # Load the rest of the settings from the form. super(GeneralSettingsForm, self).load() # Load the cache settings. cache_backend = normalize_cache_backend( self.siteconfig.get('cache_backend')) cache_type = self.CACHE_TYPES_MAP.get(cache_backend['BACKEND'], 'custom') self.fields['cache_type'].initial = cache_type if settings.DEBUG: self.fields['cache_type'].choices += (('locmem', _('Local memory cache')), ) if cache_type == 'custom': self.fields['cache_type'].choices += (('custom', _('Custom')), ) cache_locations = [] elif cache_type != 'locmem': cache_locations = cache_backend['LOCATION'] if not isinstance(cache_locations, list): cache_locations = [cache_locations] location_field = self.CACHE_LOCATION_FIELD_MAP[cache_type] self.fields[location_field].initial = ';'.join(cache_locations) # This must come after we've loaded the general settings. self.fields['server'].initial = "%s://%s" % (domain_method, site.domain) def save(self): server = self.cleaned_data['server'] if "://" not in server: # urlparse doesn't properly handle URLs without a scheme. It # believes the domain is actually the path. So we apply a prefix. server = "http://" + server url_parts = urlparse.urlparse(server) domain_method = url_parts[0] domain_name = url_parts[1] if domain_name.endswith("/"): domain_name = domain_name[:-1] site = Site.objects.get_current() site.domain = domain_name site.save() self.siteconfig.set("site_domain_method", domain_method) cache_type = self.cleaned_data['cache_type'] if cache_type != 'custom': if cache_type == 'locmem': # We want to specify a "reviewboard" location to keep items # separate from those in other caches. location = 'reviewboard' else: location_field = self.CACHE_LOCATION_FIELD_MAP[cache_type] location = self.cleaned_data[location_field] if cache_type == 'memcached': # memcached allows a list of servers, rather than just a # string representing one. location = location.split(';') self.siteconfig.set( 'cache_backend', { DEFAULT_CACHE_ALIAS: { 'BACKEND': self.CACHE_BACKENDS_MAP[cache_type], 'LOCATION': location, } }) super(GeneralSettingsForm, self).save() # Reload any important changes into the Django settings. load_site_config() def full_clean(self): cache_type = self['cache_type'].data or self['cache_type'].initial for iter_cache_type, field in self.CACHE_LOCATION_FIELD_MAP.iteritems( ): self.fields[field].required = (cache_type == iter_cache_type) return super(GeneralSettingsForm, self).full_clean() def clean_cache_host(self): cache_host = self.cleaned_data['cache_host'].strip() if self.fields['cache_host'].required and not cache_host: raise forms.ValidationError( _('A valid cache host must be provided.')) return cache_host def clean_cache_path(self): cache_path = self.cleaned_data['cache_path'].strip() if self.fields['cache_path'].required and not cache_path: raise forms.ValidationError( _('A valid cache path must be provided.')) return cache_path def clean_search_index_file(self): """Validates that the specified index file is valid.""" index_file = self.cleaned_data['search_index_file'].strip() if index_file: if not os.path.isabs(index_file): raise forms.ValidationError( _("The search index path must be absolute.")) if (os.path.exists(index_file) and not os.access(index_file, os.W_OK)): raise forms.ValidationError( _('The search index path is not writable. Make sure the ' 'web server has write access to it and its parent ' 'directory.')) return index_file class Meta: title = _("General Settings") save_blacklist = ('server', 'cache_type', 'cache_host', 'cache_path') fieldsets = ( { 'classes': ('wide', ), 'title': _("Site Settings"), 'fields': ('server', 'site_media_url', 'site_admin_name', 'site_admin_email', 'locale_timezone'), }, { 'classes': ('wide', ), 'title': _('Cache Settings'), 'fields': ('cache_type', 'cache_path', 'cache_host'), }, { 'classes': ('wide', ), 'title': _("Search"), 'fields': ('search_enable', 'search_index_file'), }, )
class GeneralSettingsForm(SiteSettingsForm): """General settings for Review Board.""" server = forms.CharField( label=_("Server"), help_text=_("The URL of this Review Board server. This should not " "contain the subdirectory Review Board is installed in."), widget=forms.TextInput(attrs={'size': '30'})) site_media_url = forms.CharField( label=_("Media URL"), help_text=_("The URL to the media files. Leave blank to use the " "default media path on this server."), required=False, widget=forms.TextInput(attrs={'size': '30'})) site_admin_name = forms.CharField( label=_("Administrator Name"), required=True, widget=forms.TextInput(attrs={'size': '30'})) site_admin_email = forms.EmailField( label=_("Administrator E-Mail"), required=True, widget=forms.TextInput(attrs={'size': '30'})) locale_timezone = TimeZoneField( label=_("Time Zone"), required=True, help_text=_("The time zone used for all dates on this server.")) search_enable = forms.BooleanField( label=_("Enable search"), help_text=_("Provides a search field for quickly searching through " "review requests."), required=False) search_index_file = forms.CharField( label=_("Search index directory"), help_text=_("The directory that search index data should be stored " "in."), required=False, widget=forms.TextInput(attrs={'size': '50'})) cache_backend = forms.CharField( label=_("Cache Backend"), help_text=_("The path to the cache backend." "Example: 'memcached://127.0.0.1:11211/'"), required=False, widget=forms.TextInput(attrs={'size': '50'})) def load(self): domain_method = self.siteconfig.get("site_domain_method") site = Site.objects.get_current() can_enable_search, reason = get_can_enable_search() if not can_enable_search: self.disabled_fields['search_enable'] = True self.disabled_fields['search_index_file'] = True self.disabled_reasons['search_enable'] = reason super(GeneralSettingsForm, self).load() # This must come after we've loaded the general settings. self.fields['server'].initial = "%s://%s" % (domain_method, site.domain) def save(self): server = self.cleaned_data['server'] if "://" not in server: # urlparse doesn't properly handle URLs without a scheme. It # believes the domain is actually the path. So we apply a prefix. server = "http://" + server url_parts = urlparse.urlparse(server) domain_method = url_parts[0] domain_name = url_parts[1] if domain_name.endswith("/"): domain_name = domain_name[:-1] site = Site.objects.get_current() site.domain = domain_name site.save() self.siteconfig.set("site_domain_method", domain_method) super(GeneralSettingsForm, self).save() # Reload any important changes into the Django settings. load_site_config() def clean_cache_backend(self): """Validates that the specified cache backend is parseable by Django.""" backend = self.cleaned_data['cache_backend'].strip() if backend: try: parse_backend_uri(backend) except InvalidCacheBackendError, e: raise forms.ValidationError(e) return backend