diff --git a/README.md b/README.md
index 668b5dd3..792f8147 100644
--- a/README.md
+++ b/README.md
@@ -71,16 +71,16 @@ Quick Start
```bash
orchestra@panel:~ ssh-copy-id root@server.address
```
- Then add the servers using the web interface `/admin/orchestration/servers`, check that the SSH connection is working and Orchestra can report the uptime of the servers.
+ Then add the servers using the web interface `/admin/orchestration/servers`, check that the SSH connection is working and Orchestra is able to report servers uptimes.
-2. Configure the services, one at a time, staring with domains, databases, webapps, websites, ...
+2. Configure your services, one at a time, staring with domains, databases, webapps, websites, ...
1. Add related [routes](orchestra/contrib/orchestration) via `/admin/orchestration/route/`
2. Configure related settings on `/admin/settings/setting/`
- 3. If required, configure related [resources](orchestra/contrib/resources) like Account disc limit, VPS traffic, etc `/resources/resource/`
- 3. Test creating and deleting service instances works as expected
+ 3. If required, configure related [resources](orchestra/contrib/resources) like *account disk limit*, *VPS traffic*, etc `/resources/resource/`
+ 3. Test if create and delete service instances works as expected
4. Do the same for the remaining services. You can disable services that you don't want by editing `INSTALLED_APPS` setting
-3. Configure billing by adding [services](orchestra/contrib/services) `/admin/services/service/add/` and [plans](orchestra/contrib/plans) `/admin/plans/plan/`. Once a service is created hit the *Update orders* button to create orders for existing service instances.
+3. Configure billing by adding [services](orchestra/contrib/services) `/admin/services/service/add/` and [plans](orchestra/contrib/plans) `/admin/plans/plan/`. Once a service is created hit the *Update orders* button to create orders for existing service instances, orders for new instances will be automatically created.
diff --git a/TODO.md b/TODO.md
index 9a2bd186..39a3827f 100644
--- a/TODO.md
+++ b/TODO.md
@@ -454,3 +454,9 @@ mkhomedir_helper or create ssh homes with bash.rc and such
# exclude from change list action, support for multiple exclusion
# breadcrumbs https://orchestra.pangea.org/admin/domains/domain/?account_id=930
+
+with open(file) as handler:
+ os.unlink(file)
+
+
+# change filter By PHP version: by detail
diff --git a/orchestra/contrib/miscellaneous/admin.py b/orchestra/contrib/miscellaneous/admin.py
index 1393034a..d240dd42 100644
--- a/orchestra/contrib/miscellaneous/admin.py
+++ b/orchestra/contrib/miscellaneous/admin.py
@@ -21,6 +21,7 @@ from .models import MiscService, Miscellaneous
class MiscServicePlugin(PluginModelAdapter):
model = MiscService
name_field = 'name'
+ plugin_field = 'service'
class MiscServiceAdmin(ExtendedModelAdmin):
@@ -56,12 +57,16 @@ class MiscServiceAdmin(ExtendedModelAdmin):
return super(MiscServiceAdmin, self).formfield_for_dbfield(db_field, **kwargs)
-class MiscellaneousAdmin(AccountAdminMixin, SelectPluginAdminMixin, admin.ModelAdmin):
+class MiscellaneousAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin):
list_display = (
'__str__', 'service_link', 'amount', 'account_link', 'dispaly_active'
)
list_filter = ('service__name', 'is_active')
list_select_related = ('service', 'account')
+ readonly_fields = ('account_link', 'service_link')
+ add_fields = ('service', 'account', 'description', 'is_active')
+ fields = ('service_link', 'account', 'description', 'is_active')
+ change_readonly_fields = ('identifier', 'service')
search_fields = ('identifier', 'description', 'account__username')
actions = (disable, enable)
plugin_field = 'service'
@@ -82,19 +87,26 @@ class MiscellaneousAdmin(AccountAdminMixin, SelectPluginAdminMixin, admin.ModelA
return obj.service
def get_fields(self, request, obj=None):
- fields = ['account', 'description', 'is_active']
- if obj is not None:
- fields = ['account_link', 'description', 'is_active']
+ fields = super().get_fields(request, obj)
+ fields = list(fields)
service = self.get_service(obj)
+ if obj:
+ fields.insert(1, 'account_link')
if service.has_amount:
fields.insert(-1, 'amount')
if service.has_identifier:
- fields.insert(1, 'identifier')
+ fields.insert(2, 'identifier')
return fields
def get_form(self, request, obj=None, **kwargs):
- form = super(SelectPluginAdminMixin, self).get_form(request, obj, **kwargs)
+ if obj:
+ plugin = self.plugin.get(obj.service.name)()
+ else:
+ plugin = self.plugin.get(self.plugin_value)()
+ self.form = plugin.get_form()
+ self.plugin_instance = plugin
service = self.get_service(obj)
+ form = super(SelectPluginAdminMixin, self).get_form(request, obj, **kwargs)
def clean_identifier(self, service=service):
identifier = self.cleaned_data['identifier']
validator_path = settings.MISCELLANEOUS_IDENTIFIER_VALIDATORS.get(service.name, None)
diff --git a/orchestra/contrib/webapps/admin.py b/orchestra/contrib/webapps/admin.py
index b3eaafe6..12e3aaee 100644
--- a/orchestra/contrib/webapps/admin.py
+++ b/orchestra/contrib/webapps/admin.py
@@ -55,7 +55,7 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin)
)
list_filter = ('type', HasWebsiteListFilter, PHPVersionListFilter)
inlines = [WebAppOptionInline]
- readonly_fields = ('account_link', )
+ readonly_fields = ('account_link',)
change_readonly_fields = ('name', 'type', 'display_websites')
search_fields = ('name', 'account__username', 'data', 'website__domains__name')
list_prefetch_related = ('content_set__website', 'content_set__website__domains')
diff --git a/orchestra/plugins/forms.py b/orchestra/plugins/forms.py
index 01286497..0394f742 100644
--- a/orchestra/plugins/forms.py
+++ b/orchestra/plugins/forms.py
@@ -1,20 +1,26 @@
from django import forms
from django.utils.encoding import force_text
+from orchestra.admin.utils import admin_link
from orchestra.forms.widgets import SpanWidget
-class PluginDataForm(forms.ModelForm):
- data = forms.CharField(widget=forms.HiddenInput, required=False)
-
+class PluginForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
- super(PluginDataForm, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
if self.plugin_field in self.fields:
value = self.plugin.get_name()
display = '%s change' % force_text(self.plugin.verbose_name)
self.fields[self.plugin_field].widget = SpanWidget(original=value, display=display)
help_text = self.fields[self.plugin_field].help_text
self.fields[self.plugin_field].help_text = getattr(self.plugin, 'help_text', help_text)
+
+
+class PluginDataForm(PluginForm):
+ data = forms.CharField(widget=forms.HiddenInput, required=False)
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
if self.instance:
for field in self.declared_fields:
initial = self.fields[field].initial
@@ -37,7 +43,7 @@ class PluginDataForm(forms.ModelForm):
self.fields[field].widget = SpanWidget(original=value, display=display)
def clean(self):
- super(PluginDataForm, self).clean()
+ super().clean()
data = {}
# Update data fields
for field in self.declared_fields:
@@ -53,3 +59,15 @@ class PluginDataForm(forms.ModelForm):
except KeyError:
data[field] = value
self.cleaned_data['data'] = data
+
+
+class PluginModelAdapterForm(PluginForm):
+ def __init__(self, *args, **kwargs):
+ super(PluginForm, self).__init__(*args, **kwargs)
+ if self.plugin_field in self.fields:
+ # Provide a link to the related DB object change view
+ value = self.plugin.related_instance.pk
+ link = admin_link()(self.plugin.related_instance)
+ display = '%s change' % link
+ self.fields[self.plugin_field].widget = SpanWidget(original=value, display=display)
+ help_text = self.fields[self.plugin_field].help_text
diff --git a/orchestra/plugins/options.py b/orchestra/plugins/options.py
index 0ff197aa..2dc4db1a 100644
--- a/orchestra/plugins/options.py
+++ b/orchestra/plugins/options.py
@@ -15,6 +15,8 @@ class Plugin(object):
def __init__(self, instance=None):
# Related model instance of this plugin
self.instance = instance
+ from .forms import PluginForm
+ self.form = PluginForm
@classmethod
def get_name(cls):
@@ -92,6 +94,11 @@ class PluginModelAdapter(Plugin):
model = None
name_field = None
+ def __init__(self, instance=None):
+ super().__init__(instance)
+ from .forms import PluginModelAdapterForm
+ self.form = PluginModelAdapterForm
+
@classmethod
def get_plugins(cls):
plugins = []