From 760d6956de03835c7816edee232f8fb8c3a88b2d Mon Sep 17 00:00:00 2001 From: Marc Aymerich Date: Mon, 21 Sep 2015 10:28:49 +0000 Subject: [PATCH] Improved SaaS wordpress form --- TODO.md | 14 ++-- orchestra/contrib/saas/backends/__init__.py | 2 +- orchestra/contrib/saas/forms.py | 76 ++++++++++++++++++++ orchestra/contrib/saas/services/bscw.py | 5 +- orchestra/contrib/saas/services/gitlab.py | 6 +- orchestra/contrib/saas/services/moodle.py | 5 +- orchestra/contrib/saas/services/options.py | 70 +----------------- orchestra/contrib/saas/services/phplist.py | 5 +- orchestra/contrib/saas/services/seafile.py | 5 +- orchestra/contrib/saas/services/wordpress.py | 18 +++-- 10 files changed, 113 insertions(+), 93 deletions(-) create mode 100644 orchestra/contrib/saas/forms.py diff --git a/TODO.md b/TODO.md index 47f1fca1..2d72feaf 100644 --- a/TODO.md +++ b/TODO.md @@ -210,18 +210,10 @@ https://code.djangoproject.com/ticket/24576 * move all tests to django-orchestra/tests * *natural keys: those fields that uniquely identify a service, list.name, website.name, webapp.name+account, make sure rest api can not edit thos things - * MultiCHoiceField proper serialization -* UNIFY PHP FPM settings name -# virtualhost name: name-account? -* add a delay to changes on the webserver apache to no overwelm it with backend executions? * replace unique_name by natural_key? * do not require contact or create default -* send signals for backend triggers -* force ignore slack billing period overridig when billing -* fpm reload starts new pools? -* rename resource.monitors to resource.backends ? * abstract model classes that enabling overriding, and ORCHESTRA_DATABASE_MODEL settings + orchestra.get_database_model() instead of explicitly importing from orchestra.contrib.databases.models import Database.. (Admin and REST API are fucked then?) # billing order list filter detect metrics that are greater from those of billing_date @@ -380,8 +372,6 @@ Case # round decimals on every billing operation -# PHPlist cron, bounces and traffic (maybe specific mail script with sitename) - # use "su $user --shell /bin/bash" on backends for security : MKDIR -p... # model.field.flatchoices @@ -396,3 +386,7 @@ Case # Implement wordpressmu change password or remove password from the form + +# Deprecate orchestra start/stop/restart services management commands? + +# Enable/disable ignore period orders list filter diff --git a/orchestra/contrib/saas/backends/__init__.py b/orchestra/contrib/saas/backends/__init__.py index 83d99b13..45a66f6c 100644 --- a/orchestra/contrib/saas/backends/__init__.py +++ b/orchestra/contrib/saas/backends/__init__.py @@ -89,7 +89,7 @@ class ApacheTrafficByHost(ServiceMonitor): sys.stderr.write(str(e)+'\\n') for opts in sites.values(): ini_date, object_id, size = opts - print object_id, size + sys.stdout.write('%s %s\n' % (object_id, size)) """).format(**context) ) diff --git a/orchestra/contrib/saas/forms.py b/orchestra/contrib/saas/forms.py new file mode 100644 index 00000000..3d317683 --- /dev/null +++ b/orchestra/contrib/saas/forms.py @@ -0,0 +1,76 @@ +from django import forms +from django.core.validators import RegexValidator +from django.utils.translation import ugettext_lazy as _ + +from orchestra.core import validators +from orchestra.forms.widgets import SpanWidget +from orchestra.plugins.forms import PluginDataForm +from orchestra.utils.python import random_ascii + + +class SaaSBaseForm(PluginDataForm): + site_url = forms.CharField(label=_("Site URL"), widget=SpanWidget(), required=False) + + class Meta: + exclude = ('database',) + readonly_fields = ('site_url',) + + def __init__(self, *args, **kwargs): + super(SaaSBaseForm, self).__init__(*args, **kwargs) + self.is_change = bool(self.instance and self.instance.pk) + if self.is_change: + site_domain = self.instance.get_site_domain() + else: + site_domain = self.plugin.site_domain + if site_domain: + site_link = '%s' % (site_domain, site_domain) + else: + site_link = '<site_name>.%s' % self.plugin.site_base_domain + self.fields['site_url'].widget.display = site_link + self.fields['name'].label = _("Site name") if self.plugin.site_base_domain else _("Username") + + +class SaaSPasswordForm(SaaSBaseForm): + password = forms.CharField(label=_("Password"), required=False, + widget=SpanWidget(display='Unknown password'), + validators=[ + validators.validate_password, + RegexValidator(r'^[^"\'\\]+$', + _('Enter a valid password. ' + 'This value may contain any ascii character except for ' + ' \'/"/\\/ characters.'), 'invalid'), + ], + help_text=_("Passwords are not stored, so there is no way to see this " + "service's password, but you can change the password using " + "this form.")) + password1 = forms.CharField(label=_("Password"), validators=[validators.validate_password], + widget=forms.PasswordInput) + password2 = forms.CharField(label=_("Password confirmation"), + widget=forms.PasswordInput, + help_text=_("Enter the same password as above, for verification.")) + + def __init__(self, *args, **kwargs): + super(SaaSPasswordForm, self).__init__(*args, **kwargs) + if self.is_change: + self.fields['password1'].required = False + self.fields['password1'].widget = forms.HiddenInput() + self.fields['password2'].required = False + self.fields['password2'].widget = forms.HiddenInput() + else: + self.fields['password'].widget = forms.HiddenInput() + self.fields['password1'].help_text = _("Suggestion: %s") % random_ascii(10) + + def clean_password2(self): + if not self.is_change: + password1 = self.cleaned_data.get("password1") + password2 = self.cleaned_data.get("password2") + if password1 and password2 and password1 != password2: + msg = _("The two password fields didn't match.") + raise forms.ValidationError(msg) + return password2 + + def save(self, commit=True): + obj = super(SoftwareServiceForm, self).save(commit=commit) + if not self.is_change: + obj.set_password(self.cleaned_data["password1"]) + return obj diff --git a/orchestra/contrib/saas/services/bscw.py b/orchestra/contrib/saas/services/bscw.py index 2bbc29be..06f1892f 100644 --- a/orchestra/contrib/saas/services/bscw.py +++ b/orchestra/contrib/saas/services/bscw.py @@ -3,10 +3,11 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers from .. import settings -from .options import SoftwareService, SoftwareServiceForm +from ..forms import SaaSPasswordForm +from .options import SoftwareService -class BSCWForm(SoftwareServiceForm): +class BSCWForm(SaaSPasswordForm): email = forms.EmailField(label=_("Email"), widget=forms.TextInput(attrs={'size':'40'})) diff --git a/orchestra/contrib/saas/services/gitlab.py b/orchestra/contrib/saas/services/gitlab.py index 1e470a83..7fc73770 100644 --- a/orchestra/contrib/saas/services/gitlab.py +++ b/orchestra/contrib/saas/services/gitlab.py @@ -4,12 +4,12 @@ from rest_framework import serializers from orchestra.forms import widgets -from .options import SoftwareService, SoftwareServiceForm - from .. import settings +from ..forms import SaaSPasswordForm +from .options import SoftwareService -class GitLabForm(SoftwareServiceForm): +class GitLabForm(SaaSPasswordForm): email = forms.EmailField(label=_("Email"), help_text=_("Initial email address, changes on the GitLab server are not reflected here.")) diff --git a/orchestra/contrib/saas/services/moodle.py b/orchestra/contrib/saas/services/moodle.py index 72e39bdc..88f5e623 100644 --- a/orchestra/contrib/saas/services/moodle.py +++ b/orchestra/contrib/saas/services/moodle.py @@ -1,10 +1,11 @@ from django import forms from django.utils.translation import ugettext_lazy as _ -from .options import SoftwareService, SoftwareServiceForm +from ..forms import SaaSPasswordForm +from .options import SoftwareService -class MoodleForm(SoftwareServiceForm): +class MoodleForm(SaaSPasswordForm): email = forms.EmailField(label=_("Email")) diff --git a/orchestra/contrib/saas/services/options.py b/orchestra/contrib/saas/services/options.py index 3024905f..cdc40a62 100644 --- a/orchestra/contrib/saas/services/options.py +++ b/orchestra/contrib/saas/services/options.py @@ -1,81 +1,17 @@ -from django import forms from django.core.exceptions import ValidationError -from django.core.validators import RegexValidator from django.utils.translation import ugettext_lazy as _ from orchestra import plugins from orchestra.contrib.orchestration import Operation -from orchestra.core import validators -from orchestra.forms.widgets import SpanWidget -from orchestra.plugins.forms import PluginDataForm from orchestra.utils.functional import cached -from orchestra.utils.python import import_class, random_ascii +from orchestra.utils.python import import_class from .. import settings - - -class SoftwareServiceForm(PluginDataForm): - site_url = forms.CharField(label=_("Site URL"), widget=SpanWidget(), required=False) - password = forms.CharField(label=_("Password"), required=False, - widget=SpanWidget(display='Unknown password'), - validators=[ - validators.validate_password, - RegexValidator(r'^[^"\'\\]+$', - _('Enter a valid password. ' - 'This value may contain any ascii character except for ' - ' \'/"/\\/ characters.'), 'invalid'), - ], - help_text=_("Passwords are not stored, so there is no way to see this " - "service's password, but you can change the password using " - "this form.")) - password1 = forms.CharField(label=_("Password"), validators=[validators.validate_password], - widget=forms.PasswordInput) - password2 = forms.CharField(label=_("Password confirmation"), - widget=forms.PasswordInput, - help_text=_("Enter the same password as above, for verification.")) - - class Meta: - exclude = ('database',) - readonly_fields = ('site_url',) - - def __init__(self, *args, **kwargs): - super(SoftwareServiceForm, self).__init__(*args, **kwargs) - self.is_change = bool(self.instance and self.instance.pk) - if self.is_change: - site_domain = self.instance.get_site_domain() - self.fields['password1'].required = False - self.fields['password1'].widget = forms.HiddenInput() - self.fields['password2'].required = False - self.fields['password2'].widget = forms.HiddenInput() - else: - self.fields['password'].widget = forms.HiddenInput() - self.fields['password1'].help_text = _("Suggestion: %s") % random_ascii(10) - site_domain = self.plugin.site_domain - if site_domain: - site_link = '%s' % (site_domain, site_domain) - else: - site_link = '<site_name>.%s' % self.plugin.site_base_domain - self.fields['site_url'].widget.display = site_link - self.fields['name'].label = _("Site name") if self.plugin.site_base_domain else _("Username") - - def clean_password2(self): - if not self.is_change: - password1 = self.cleaned_data.get("password1") - password2 = self.cleaned_data.get("password2") - if password1 and password2 and password1 != password2: - msg = _("The two password fields didn't match.") - raise forms.ValidationError(msg) - return password2 - - def save(self, commit=True): - obj = super(SoftwareServiceForm, self).save(commit=commit) - if not self.is_change: - obj.set_password(self.cleaned_data["password1"]) - return obj +from ..forms import SaaSPasswordForm class SoftwareService(plugins.Plugin): - form = SoftwareServiceForm + form = SaaSPasswordForm site_domain = None site_base_domain = None has_custom_domain = False diff --git a/orchestra/contrib/saas/services/phplist.py b/orchestra/contrib/saas/services/phplist.py index 0bcd4d4a..555ec816 100644 --- a/orchestra/contrib/saas/services/phplist.py +++ b/orchestra/contrib/saas/services/phplist.py @@ -8,10 +8,11 @@ from orchestra.contrib.databases.models import Database, DatabaseUser from orchestra.forms.widgets import SpanWidget from .. import settings -from .options import SoftwareService, SoftwareServiceForm +from ..forms import SaaSPasswordForm +from .options import SoftwareService -class PHPListForm(SoftwareServiceForm): +class PHPListForm(SaaSPasswordForm): admin_username = forms.CharField(label=_("Admin username"), required=False, widget=SpanWidget(display='admin')) diff --git a/orchestra/contrib/saas/services/seafile.py b/orchestra/contrib/saas/services/seafile.py index 3484e2c6..da736142 100644 --- a/orchestra/contrib/saas/services/seafile.py +++ b/orchestra/contrib/saas/services/seafile.py @@ -3,12 +3,13 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers from .. import settings -from .options import SoftwareService, SoftwareServiceForm +from ..forms import SaaSPasswordForm +from .options import SoftwareService # TODO monitor quota since out of sync? -class SeaFileForm(SoftwareServiceForm): +class SeaFileForm(SaaSPasswordForm): email = forms.EmailField(label=_("Email"), widget=forms.TextInput(attrs={'size':'40'})) quota = forms.IntegerField(label=_("Quota"), initial=settings.SAAS_SEAFILE_DEFAULT_QUOTA, help_text=_("Disk quota in MB.")) diff --git a/orchestra/contrib/saas/services/wordpress.py b/orchestra/contrib/saas/services/wordpress.py index afca5222..021ec464 100644 --- a/orchestra/contrib/saas/services/wordpress.py +++ b/orchestra/contrib/saas/services/wordpress.py @@ -1,13 +1,23 @@ from django import forms +from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers -from .options import SoftwareService, SoftwareServiceForm +from .options import SoftwareService +from ..forms import SaaSBaseForm -class WordPressForm(SoftwareServiceForm): - email = forms.EmailField(label=_("Email"), widget=forms.TextInput(attrs={'size':'40'})) - +class WordPressForm(SaaSBaseForm): + email = forms.EmailField(label=_("Email"), widget=forms.TextInput(attrs={'size':'40'}), + help_text=_("A new user will be created if the above email address is not in the database.
" + "The username and password will be mailed to this email address.")) + + def __init__(self, *args, **kwargs): + super(WordPressForm, self).__init__(*args, **kwargs) + if self.is_change: + admin_url = 'http://%s/wp-admin/' % self.instance.get_site_domain() + help_text = 'Admin URL: {0}'.format(admin_url) + self.fields['site_url'].help_text = mark_safe(help_text) class WordPressDataSerializer(serializers.Serializer): email = serializers.EmailField(label=_("Email"))