Compare commits

..

No commits in common. "master" and "musician" have entirely different histories.

413 changed files with 2501 additions and 6091 deletions

View File

@ -0,0 +1,48 @@
# Generated by Django 2.2.28 on 2023-07-22 08:01
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import orchestra.core.validators
class Migration(migrations.Migration):
initial = True
dependencies = [
('orchestration', '__first__'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='SystemUser',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('username', models.CharField(help_text='Required. 32 characters or fewer. Letters, digits and ./-/_ only.', max_length=32, unique=True, validators=[orchestra.core.validators.validate_username], verbose_name='username')),
('password', models.CharField(max_length=128, verbose_name='password')),
('home', models.CharField(blank=True, help_text='Starting location when login with this no-shell user.', max_length=256, verbose_name='home')),
('directory', models.CharField(blank=True, help_text="Optional directory relative to user's home.", max_length=256, verbose_name='directory')),
('shell', models.CharField(choices=[('/dev/null', 'No shell, FTP only'), ('/bin/rssh', 'No shell, SFTP/RSYNC only'), ('/usr/bin/git-shell', 'No shell, GIT only'), ('/bin/bash', '/bin/bash')], default='/dev/null', max_length=32, verbose_name='shell')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this account should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='systemusers', to=settings.AUTH_USER_MODEL, verbose_name='Account')),
('groups', models.ManyToManyField(blank=True, help_text='A new group will be created for the user. Which additional groups would you like them to be a member of?', to='systemusers.SystemUser')),
],
),
migrations.CreateModel(
name='WebappUsers',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('username', models.CharField(help_text='Required. 32 characters or fewer. Letters, digits and ./-/_ only.', max_length=32, unique=True, validators=[orchestra.core.validators.validate_username], verbose_name='username')),
('password', models.CharField(max_length=128, verbose_name='password')),
('home', models.CharField(blank=True, help_text='Starting location when login with this no-shell user.', max_length=256, verbose_name='home')),
('shell', models.CharField(choices=[('/dev/null', 'No shell, FTP only'), ('/bin/rssh', 'No shell, SFTP/RSYNC only'), ('/usr/bin/git-shell', 'No shell, GIT only'), ('/bin/bash', '/bin/bash')], default='/dev/null', max_length=32, verbose_name='shell')),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='accounts', to=settings.AUTH_USER_MODEL, verbose_name='Account')),
('target_server', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='orchestration.Server', verbose_name='Server')),
],
options={
'unique_together': {('username', 'target_server')},
},
),
]

View File

@ -0,0 +1,43 @@
from django import forms
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.core.exceptions import ValidationError
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
from orchestra.core import validators
from orchestra.contrib.systemusers.models import WebappUsers
from .models import WebApp
class WebappCreationForm(forms.ModelForm):
username = forms.CharField(label=_("Username"), max_length=16,
required=False, validators=[validators.validate_name],
help_text=_("Required. 16 characters or fewer. Letters, digits and "
"@/./+/-/_ only."),
error_messages={
'invalid': _("This value may contain 16 characters or fewer, only letters, numbers and "
"@/./+/-/_ characters.")})
user = forms.ModelChoiceField(required=False, queryset=WebappUsers.objects)
password1 = forms.CharField(label=_("Password"), required=False,
widget=forms.PasswordInput(attrs={'autocomplete': 'off'}),
validators=[validators.validate_password])
password2 = forms.CharField(label=_("Password confirmation"), required=False,
widget=forms.PasswordInput,
help_text=_("Enter the same password as above, for verification."))
class Meta:
model = WebApp
fields = ('username', 'account', 'type')
def __init__(self, *args, **kwargs):
super(WebappCreationForm, self).__init__(*args, **kwargs)
def clean_password2(self):
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 ValidationError(msg)
return password2

View File

@ -1,6 +1,4 @@
# -*- coding: utf-8 -*- # Generated by Django 2.2.28 on 2023-08-17 09:14
# Generated by Django 1.10.5 on 2021-04-22 11:25
from __future__ import unicode_literals
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
@ -10,15 +8,13 @@ import orchestra.core.validators
class Migration(migrations.Migration): class Migration(migrations.Migration):
replaces = [('websites', '0001_initial'), ('websites', '0002_auto_20160219_1036'), ('websites', '0003_auto_20170528_2011'), ('websites', '0004_auto_20170625_1813'), ('websites', '0005_auto_20170625_1840'), ('websites', '0006_auto_20170625_1840'), ('websites', '0007_auto_20170625_1840'), ('websites', '0008_auto_20170625_1841'), ('websites', '0009_auto_20170625_2206'), ('websites', '0010_auto_20170625_2214'), ('websites', '0011_auto_20170704_1117'), ('websites', '0012_auto_20190805_1134'), ('websites', '0013_auto_20200204_1217'), ('websites', '0014_auto_20200204_1218'), ('websites', '0015_auto_20200204_1219'), ('websites', '0016_auto_20200204_1221'), ('websites', '0017_auto_20210330_1049')]
initial = True initial = True
dependencies = [ dependencies = [
('domains', '0001_initial'),
('webapps', '0001_initial'),
('orchestration', '0007_auto_20170528_2011'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('orchestration', '__first__'),
('domains', '__first__'),
('webapps', '0004_auto_20230817_1108'),
] ]
operations = [ operations = [
@ -37,10 +33,15 @@ class Migration(migrations.Migration):
('name', models.CharField(max_length=128, validators=[orchestra.core.validators.validate_name], verbose_name='name')), ('name', models.CharField(max_length=128, validators=[orchestra.core.validators.validate_name], verbose_name='name')),
('protocol', models.CharField(choices=[('http', 'HTTP'), ('https', 'HTTPS'), ('http/https', 'HTTP and HTTPS'), ('https-only', 'HTTPS only')], default='http', help_text='Select the protocol(s) for this website<br><tt>HTTPS only</tt> performs a redirection from <tt>http</tt> to <tt>https</tt>.', max_length=16, verbose_name='protocol')), ('protocol', models.CharField(choices=[('http', 'HTTP'), ('https', 'HTTPS'), ('http/https', 'HTTP and HTTPS'), ('https-only', 'HTTPS only')], default='http', help_text='Select the protocol(s) for this website<br><tt>HTTPS only</tt> performs a redirection from <tt>http</tt> to <tt>https</tt>.', max_length=16, verbose_name='protocol')),
('is_active', models.BooleanField(default=True, verbose_name='active')), ('is_active', models.BooleanField(default=True, verbose_name='active')),
('comments', models.TextField(blank=True, default='')),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='websites', to=settings.AUTH_USER_MODEL, verbose_name='Account')), ('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='websites', to=settings.AUTH_USER_MODEL, verbose_name='Account')),
('contents', models.ManyToManyField(through='websites.Content', to='webapps.WebApp')), ('contents', models.ManyToManyField(through='websites.Content', to='webapps.WebApp')),
('domains', models.ManyToManyField(related_name='websites', to='domains.Domain', verbose_name='domains')), ('domains', models.ManyToManyField(blank=True, related_name='websites', to='domains.Domain', verbose_name='domains')),
('target_server', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='websites', to='orchestration.Server', verbose_name='Target Server')),
], ],
options={
'unique_together': {('name', 'account', 'target_server')},
},
), ),
migrations.CreateModel( migrations.CreateModel(
name='WebsiteDirective', name='WebsiteDirective',
@ -56,28 +57,8 @@ class Migration(migrations.Migration):
name='website', name='website',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='websites.Website', verbose_name='web site'), field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='websites.Website', verbose_name='web site'),
), ),
migrations.AlterField(
model_name='website',
name='domains',
field=models.ManyToManyField(blank=True, related_name='websites', to='domains.Domain', verbose_name='domains'),
),
migrations.AddField(
model_name='website',
name='target_server',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='websites', to='orchestration.Server', verbose_name='Target Server'),
preserve_default=False,
),
migrations.AddField(
model_name='website',
name='comments',
field=models.TextField(blank=True, default=''),
),
migrations.AlterUniqueTogether(
name='website',
unique_together=set([('name', 'account')]),
),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='content', name='content',
unique_together=set([('website', 'path')]), unique_together={('website', 'path')},
), ),
] ]

View File

@ -0,0 +1,64 @@
# Generated by Django 2.2.28 on 2023-08-17 09:42
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import orchestra.core.validators
class Migration(migrations.Migration):
initial = True
dependencies = [
('orchestration', '__first__'),
('domains', '__first__'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('webapps', '0004_auto_20230817_1108'),
]
operations = [
migrations.CreateModel(
name='Content',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('path', models.CharField(blank=True, max_length=256, validators=[orchestra.core.validators.validate_url_path], verbose_name='path')),
('webapp', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='webapps.WebApp', verbose_name='web application')),
],
),
migrations.CreateModel(
name='Website',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=128, validators=[orchestra.core.validators.validate_name], verbose_name='name')),
('protocol', models.CharField(choices=[('http', 'HTTP'), ('https', 'HTTPS'), ('http/https', 'HTTP and HTTPS'), ('https-only', 'HTTPS only')], default='http', help_text='Select the protocol(s) for this website<br><tt>HTTPS only</tt> performs a redirection from <tt>http</tt> to <tt>https</tt>.', max_length=16, verbose_name='protocol')),
('is_active', models.BooleanField(default=True, verbose_name='active')),
('comments', models.TextField(blank=True, default='')),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='websites', to=settings.AUTH_USER_MODEL, verbose_name='Account')),
('contents', models.ManyToManyField(through='websites.Content', to='webapps.WebApp')),
('domains', models.ManyToManyField(blank=True, related_name='websites', to='domains.Domain', verbose_name='domains')),
('target_server', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='websites', to='orchestration.Server', verbose_name='Target Server')),
],
options={
'unique_together': {('name', 'account')},
},
),
migrations.CreateModel(
name='WebsiteDirective',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(choices=[(None, '-------'), ('HTTPD', [('redirect', 'Redirection'), ('proxy', 'Proxy'), ('error-document', 'ErrorDocumentRoot')]), ('SSL', [('ssl-ca', 'SSL CA'), ('ssl-cert', 'SSL cert'), ('ssl-key', 'SSL key')]), ('ModSecurity', [('sec-rule-remove', 'SecRuleRemoveById'), ('sec-engine', 'SecRuleEngine Off')]), ('SaaS', [('wordpress-saas', 'WordPress SaaS'), ('dokuwiki-saas', 'DokuWiki SaaS'), ('drupal-saas', 'Drupdal SaaS'), ('moodle-saas', 'Moodle SaaS')])], db_index=True, max_length=128, verbose_name='name')),
('value', models.CharField(blank=True, max_length=256, verbose_name='value')),
('website', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='directives', to='websites.Website', verbose_name='web site')),
],
),
migrations.AddField(
model_name='content',
name='website',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='websites.Website', verbose_name='web site'),
),
migrations.AlterUniqueTogether(
name='content',
unique_together={('website', 'path')},
),
]

View File

@ -0,0 +1,64 @@
# Generated by Django 2.2.28 on 2023-08-17 09:08
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import orchestra.core.validators
class Migration(migrations.Migration):
initial = True
dependencies = [
('domains', '__first__'),
('orchestration', '__first__'),
('webapps', '0004_auto_20230817_1108'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Content',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('path', models.CharField(blank=True, max_length=256, validators=[orchestra.core.validators.validate_url_path], verbose_name='path')),
('webapp', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='webapps.WebApp', verbose_name='web application')),
],
),
migrations.CreateModel(
name='Website',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=128, validators=[orchestra.core.validators.validate_name], verbose_name='name')),
('protocol', models.CharField(choices=[('http', 'HTTP'), ('https', 'HTTPS'), ('http/https', 'HTTP and HTTPS'), ('https-only', 'HTTPS only')], default='http', help_text='Select the protocol(s) for this website<br><tt>HTTPS only</tt> performs a redirection from <tt>http</tt> to <tt>https</tt>.', max_length=16, verbose_name='protocol')),
('is_active', models.BooleanField(default=True, verbose_name='active')),
('comments', models.TextField(blank=True, default='')),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='websites', to=settings.AUTH_USER_MODEL, verbose_name='Account')),
('contents', models.ManyToManyField(through='websites.Content', to='webapps.WebApp')),
('domains', models.ManyToManyField(blank=True, related_name='websites', to='domains.Domain', verbose_name='domains')),
('target_server', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='websites', to='orchestration.Server', verbose_name='Target Server')),
],
options={
'unique_together': {('name', 'account', 'target_server')},
},
),
migrations.CreateModel(
name='WebsiteDirective',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(choices=[(None, '-------'), ('HTTPD', [('redirect', 'Redirection'), ('proxy', 'Proxy'), ('error-document', 'ErrorDocumentRoot')]), ('SSL', [('ssl-ca', 'SSL CA'), ('ssl-cert', 'SSL cert'), ('ssl-key', 'SSL key')]), ('ModSecurity', [('sec-rule-remove', 'SecRuleRemoveById'), ('sec-engine', 'SecRuleEngine Off')]), ('SaaS', [('wordpress-saas', 'WordPress SaaS'), ('dokuwiki-saas', 'DokuWiki SaaS'), ('drupal-saas', 'Drupdal SaaS'), ('moodle-saas', 'Moodle SaaS')])], db_index=True, max_length=128, verbose_name='name')),
('value', models.CharField(blank=True, max_length=256, verbose_name='value')),
('website', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='directives', to='websites.Website', verbose_name='web site')),
],
),
migrations.AddField(
model_name='content',
name='website',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='websites.Website', verbose_name='web site'),
),
migrations.AlterUniqueTogether(
name='content',
unique_together={('website', 'path')},
),
]

View File

@ -0,0 +1,3 @@
[Trash Info]
Path=orchestra/contrib/systemusers/migrations/0001_initial.py
DeletionDate=2023-07-22T10:04:42

View File

@ -0,0 +1,3 @@
[Trash Info]
Path=orchestra/contrib/webapps/forms.py
DeletionDate=2023-07-24T18:44:25

View File

@ -0,0 +1,3 @@
[Trash Info]
Path=orchestra/contrib/websites/migrations
DeletionDate=2023-08-17T11:42:31

View File

@ -0,0 +1,3 @@
[Trash Info]
Path=orchestra/contrib/websites/migrations
DeletionDate=2023-08-17T11:45:11

View File

@ -0,0 +1,3 @@
[Trash Info]
Path=orchestra/contrib/websites/migrations
DeletionDate=2023-08-17T11:14:47

View File

@ -0,0 +1,42 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<value>
<list size="24">
<item index="0" class="java.lang.String" itemvalue="paramiko" />
<item index="1" class="java.lang.String" itemvalue="fabric" />
<item index="2" class="java.lang.String" itemvalue="django-admin-tools" />
<item index="3" class="java.lang.String" itemvalue="django-fluent-dashboard" />
<item index="4" class="java.lang.String" itemvalue="phonenumbers" />
<item index="5" class="java.lang.String" itemvalue="jsonfield" />
<item index="6" class="java.lang.String" itemvalue="amqp" />
<item index="7" class="java.lang.String" itemvalue="python-dateutil" />
<item index="8" class="java.lang.String" itemvalue="django-localflavor" />
<item index="9" class="java.lang.String" itemvalue="ecdsa" />
<item index="10" class="java.lang.String" itemvalue="kombu" />
<item index="11" class="java.lang.String" itemvalue="django-extensions" />
<item index="12" class="java.lang.String" itemvalue="celery" />
<item index="13" class="java.lang.String" itemvalue="django-countries" />
<item index="14" class="java.lang.String" itemvalue="Pygments" />
<item index="15" class="java.lang.String" itemvalue="django-filter" />
<item index="16" class="java.lang.String" itemvalue="Django" />
<item index="17" class="java.lang.String" itemvalue="anyjson" />
<item index="18" class="java.lang.String" itemvalue="django-celery" />
<item index="19" class="java.lang.String" itemvalue="djangorestframework" />
<item index="20" class="java.lang.String" itemvalue="Markdown" />
<item index="21" class="java.lang.String" itemvalue="django-iban" />
<item index="22" class="java.lang.String" itemvalue="billiard" />
<item index="23" class="java.lang.String" itemvalue="passlib" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile>
</component>

7
.idea/misc.xml Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.7 (zfs-blues)" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/orchestra.iml" filepath="$PROJECT_DIR$/.idea/orchestra.iml" />
</modules>
</component>
</project>

32
.idea/orchestra.iml Normal file
View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="FacetManager">
<facet type="django" name="Django">
<configuration>
<option name="rootFolder" value="$MODULE_DIR$/orchestra/conf/project_template" />
<option name="settingsModule" value="orchestra/settings.py" />
<option name="manageScript" value="manage.py" />
<option name="environment" value="&lt;map/&gt;" />
<option name="doNotUseTestRunner" value="false" />
<option name="trackFilePattern" value="migrations" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TemplatesService">
<option name="TEMPLATE_CONFIGURATION" value="Django" />
<option name="TEMPLATE_FOLDERS">
<list>
<option value="$MODULE_DIR$/orchestra/contrib/bills/templates" />
</list>
</option>
</component>
<component name="TestRunnerService">
<option name="projectConfiguration" value="pytest" />
<option name="PROJECT_TEST_RUNNER" value="pytest" />
</component>
</module>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

261
.idea/workspace.xml Normal file
View File

@ -0,0 +1,261 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="573278bf-2cdd-4afd-973a-1660f3364e9d" name="Default Changelist" comment="">
<change beforePath="$PROJECT_DIR$/orchestra/api/actions.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/api/actions.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/api/serializers.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/api/serializers.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/accounts/models.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/accounts/models.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/accounts/serializers.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/accounts/serializers.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/bills/admin.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/bills/admin.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/bills/locale/ca/LC_MESSAGES/django.mo" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/bills/locale/ca/LC_MESSAGES/django.mo" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/bills/locale/ca/LC_MESSAGES/django.po" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/bills/locale/ca/LC_MESSAGES/django.po" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/bills/locale/es/LC_MESSAGES/django.mo" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/bills/locale/es/LC_MESSAGES/django.mo" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/bills/locale/es/LC_MESSAGES/django.po" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/bills/locale/es/LC_MESSAGES/django.po" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/bills/models.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/bills/models.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/bills/settings.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/bills/settings.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/bills/templates/bills/microspective.css" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/bills/templates/bills/microspective.css" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/bills/templates/bills/microspective.html" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/bills/templates/bills/microspective.html" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/databases/admin.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/databases/admin.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/databases/models.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/databases/models.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/domains/admin.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/domains/admin.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/domains/backends.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/domains/backends.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/domains/forms.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/domains/forms.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/domains/models.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/domains/models.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/domains/settings.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/domains/settings.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/domains/validators.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/domains/validators.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/lists/api.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/lists/api.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/lists/backends.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/lists/backends.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/lists/serializers.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/lists/serializers.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/mailboxes/backends.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/mailboxes/backends.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/orchestration/backends.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/orchestration/backends.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/payments/actions.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/payments/actions.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/payments/methods/sepadirectdebit.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/payments/methods/sepadirectdebit.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/saas/models.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/saas/models.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/saas/services/helpers.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/saas/services/helpers.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/systemusers/backends.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/systemusers/backends.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/systemusers/models.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/systemusers/models.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/webapps/backends/php.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/webapps/backends/php.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/webapps/models.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/webapps/models.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/webapps/options.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/webapps/options.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/webapps/serializers.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/webapps/serializers.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/webapps/signals.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/webapps/signals.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/websites/admin.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/websites/admin.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/websites/backends/apache.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/websites/backends/apache.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/orchestra/contrib/websites/models.py" beforeDir="false" afterPath="$PROJECT_DIR$/orchestra/contrib/websites/models.py" afterDir="false" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileEditorManager">
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/orchestra/contrib/lists/backends.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="589">
<caret line="124" column="4" lean-forward="true" selection-start-line="124" selection-start-column="4" selection-end-line="124" selection-end-column="4" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/orchestra/contrib/orchestration/backends.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="4256">
<caret line="224" column="8" selection-start-line="224" selection-start-column="8" selection-end-line="224" selection-end-column="8" />
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="FindInProjectRecents">
<findStrings>
<find>check_origin</find>
<find>updated_</find>
<find>UPDATED_CHECK_ORIGIN</find>
</findStrings>
<dirStrings>
<dir>$PROJECT_DIR$/orchestra/contrib/lists</dir>
</dirStrings>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/orchestra/contrib/lists/backends.py" />
</list>
</option>
</component>
<component name="ProjectConfigurationFiles">
<option name="files">
<list>
<option value="$PROJECT_DIR$/.idea/orchestra.iml" />
<option value="$PROJECT_DIR$/.idea/misc.xml" />
<option value="$PROJECT_DIR$/.idea/modules.xml" />
<option value="$PROJECT_DIR$/.idea/vcs.xml" />
<option value="$PROJECT_DIR$/.idea/inspectionProfiles/Project_Default.xml" />
</list>
</option>
</component>
<component name="ProjectFrameBounds" extendedState="6">
<option name="x" value="959" />
<option name="width" value="961" />
<option name="height" value="1056" />
</component>
<component name="ProjectView">
<navigator proportions="" version="1">
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="orchestra" type="b2602c69:ProjectViewProjectNode" />
<item name="orchestra" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="orchestra" type="b2602c69:ProjectViewProjectNode" />
<item name="orchestra" type="462c0819:PsiDirectoryNode" />
<item name="orchestra" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="orchestra" type="b2602c69:ProjectViewProjectNode" />
<item name="orchestra" type="462c0819:PsiDirectoryNode" />
<item name="orchestra" type="462c0819:PsiDirectoryNode" />
<item name="contrib" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="orchestra" type="b2602c69:ProjectViewProjectNode" />
<item name="orchestra" type="462c0819:PsiDirectoryNode" />
<item name="orchestra" type="462c0819:PsiDirectoryNode" />
<item name="contrib" type="462c0819:PsiDirectoryNode" />
<item name="lists" type="462c0819:PsiDirectoryNode" />
</path>
</expand>
<select />
</subPane>
</pane>
<pane id="Scope" />
</panes>
</component>
<component name="PropertiesComponent">
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="node.js.detected.package.eslint" value="true" />
<property name="node.js.detected.package.tslint" value="true" />
<property name="node.js.path.for.package.eslint" value="project" />
<property name="node.js.path.for.package.tslint" value="project" />
<property name="node.js.selected.package.eslint" value="(autodetect)" />
<property name="node.js.selected.package.tslint" value="(autodetect)" />
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="RunManager">
<configuration name="orchestra" type="Python.DjangoServer" factoryName="Django server">
<module name="orchestra" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="launchJavascriptDebuger" value="false" />
<option name="port" value="8000" />
<option name="host" value="" />
<option name="additionalOptions" value="" />
<option name="browserUrl" value="" />
<option name="runTestServer" value="false" />
<option name="runNoReload" value="false" />
<option name="useCustomRunCommand" value="false" />
<option name="customRunCommand" value="" />
<method v="2" />
</configuration>
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="573278bf-2cdd-4afd-973a-1660f3364e9d" name="Default Changelist" comment="" />
<created>1582799032840</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1582799032840</updated>
<workItem from="1582799053215" duration="2986000" />
<workItem from="1582882260836" duration="4157000" />
<workItem from="1582890671958" duration="815000" />
</task>
<servers />
</component>
<component name="TimeTrackingManager">
<option name="totallyTimeSpent" value="7958000" />
</component>
<component name="ToolWindowManager">
<frame x="-1" y="0" width="1922" height="1057" extended-state="6" />
<editor active="true" />
<layout>
<window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.124068156" />
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
<window_info id="Favorites" order="2" side_tool="true" />
<window_info anchor="bottom" id="Message" order="0" />
<window_info anchor="bottom" id="Find" order="1" />
<window_info anchor="bottom" id="Run" order="2" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.4" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
<window_info anchor="bottom" id="TODO" order="6" />
<window_info anchor="bottom" id="Docker" order="7" show_stripe_button="false" />
<window_info anchor="bottom" id="Version Control" order="8" />
<window_info anchor="bottom" id="Database Changes" order="9" />
<window_info anchor="bottom" id="Event Log" order="10" side_tool="true" />
<window_info anchor="bottom" id="Terminal" order="11" />
<window_info anchor="bottom" id="Python Console" order="12" />
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
<window_info anchor="right" id="SciView" order="3" />
<window_info anchor="right" id="Database" order="4" />
</layout>
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="1" />
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/orchestra/contrib/lists/backends.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="589">
<caret line="124" column="4" lean-forward="true" selection-start-line="124" selection-start-column="4" selection-end-line="124" selection-end-column="4" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/orchestra/contrib/orchestration/backends.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="4256">
<caret line="224" column="8" selection-start-line="224" selection-start-column="8" selection-end-line="224" selection-end-column="8" />
</state>
</provider>
</entry>
</component>
</project>

View File

@ -170,10 +170,10 @@ django-admin.py compilemessages -l ca
https://docs.djangoproject.com/en/1.7/topics/i18n/translation/#joining-strings-string-concat https://docs.djangoproject.com/en/1.7/topics/i18n/translation/#joining-strings-string-concat
from django.utils.translation import ugettext from django.utils.translation import gettext
from django.utils import translation from django.utils import translation
translation.activate('ca') translation.activate('ca')
ugettext("Description") gettext("Description")
* saas validate_creation generic approach, for all backends. standard output * saas validate_creation generic approach, for all backends. standard output

View File

@ -59,7 +59,7 @@
```python ```python
import os import os
import textwrap import textwrap
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.contrib.orchestration import ServiceController, replace from orchestra.contrib.orchestration import ServiceController, replace
from orchestra.contrib.resources import ServiceMonitor from orchestra.contrib.resources import ServiceMonitor

View File

@ -6,7 +6,7 @@ from django.contrib import admin
from django.urls import reverse from django.urls import reverse
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from .dashboard import * from .dashboard import *
from .options import * from .options import *

View File

@ -3,7 +3,7 @@ from functools import partial
from django.contrib import admin from django.contrib import admin
from django.core.mail import send_mass_mail from django.core.mail import send_mass_mail
from django.shortcuts import render from django.shortcuts import render
from django.utils.translation import ungettext, ugettext_lazy as _ from django.utils.translation import ngettext, gettext_lazy as _
from .. import settings from .. import settings
@ -81,7 +81,7 @@ class SendEmail(object):
if extra_to: if extra_to:
emails.append((subject, message, email_from, extra_to)) emails.append((subject, message, email_from, extra_to))
send_mass_mail(emails, fail_silently=False) send_mass_mail(emails, fail_silently=False)
msg = ungettext( msg = ngettext(
_("Message has been sent to one %s.") % self.opts.verbose_name_plural, _("Message has been sent to one %s.") % self.opts.verbose_name_plural,
_("Message has been sent to %i %s.") % (num, self.opts.verbose_name_plural), _("Message has been sent to %i %s.") % (num, self.opts.verbose_name_plural),
num num
@ -124,7 +124,7 @@ def base_disable(modeladmin, request, queryset, disable=True):
'verbose_name_plural': opts.verbose_name_plural, 'verbose_name_plural': opts.verbose_name_plural,
'num': num 'num': num
} }
msg = ungettext( msg = ngettext(
_("Selected %(verbose_name)s and related services has been %(action_name)s.") % context, _("Selected %(verbose_name)s and related services has been %(action_name)s.") % context,
_("%(num)s selected %(verbose_name_plural)s and related services have been %(action_name)s.") % context, _("%(num)s selected %(verbose_name_plural)s and related services have been %(action_name)s.") % context,
num) num)

View File

@ -1,5 +1,5 @@
from django.urls import reverse from django.urls import reverse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from fluent_dashboard import dashboard, appsettings from fluent_dashboard import dashboard, appsettings
from fluent_dashboard.modules import CmsAppIconList from fluent_dashboard.modules import CmsAppIconList

View File

@ -4,11 +4,10 @@ from django.contrib import messages
from django.contrib.admin import helpers from django.contrib.admin import helpers
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.utils.decorators import available_attrs from django.utils.encoding import force_str
from django.utils.encoding import force_text
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
def admin_field(method): def admin_field(method):
@ -50,7 +49,7 @@ def action_with_confirmation(action_name=None, extra_context=None, validator=Non
""" """
def decorator(func, extra_context=extra_context, template=template, action_name=action_name, validatior=validator): def decorator(func, extra_context=extra_context, template=template, action_name=action_name, validatior=validator):
@wraps(func, assigned=available_attrs(func)) @wraps(func)
def inner(modeladmin, request, queryset, action_name=action_name, extra_context=extra_context, validator=validator): def inner(modeladmin, request, queryset, action_name=action_name, extra_context=extra_context, validator=validator):
if validator is not None: if validator is not None:
try: try:
@ -69,10 +68,10 @@ def action_with_confirmation(action_name=None, extra_context=None, validator=Non
action_value = func.__name__ action_value = func.__name__
if len(queryset) == 1: if len(queryset) == 1:
objects_name = force_text(opts.verbose_name) objects_name = force_str(opts.verbose_name)
obj = queryset.get() obj = queryset.get()
else: else:
objects_name = force_text(opts.verbose_name_plural) objects_name = force_str(opts.verbose_name_plural)
obj = None obj = None
if not action_name: if not action_name:
action_name = func.__name__ action_name = func.__name__

View File

@ -5,8 +5,8 @@ from django import forms
from django.contrib.admin import helpers from django.contrib.admin import helpers
from django.core import validators from django.core import validators
from django.forms.models import modelformset_factory, BaseModelFormSet from django.forms.models import modelformset_factory, BaseModelFormSet
from django.template import Template from django.template import Template, Context
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.forms.widgets import SpanWidget from orchestra.forms.widgets import SpanWidget
@ -31,7 +31,7 @@ class AdminFormMixin(object):
context = { context = {
'adminform': adminform 'adminform': adminform
} }
return template.render(context) return template.render(Context(context))
class AdminFormSet(BaseModelFormSet): class AdminFormSet(BaseModelFormSet):
@ -74,7 +74,7 @@ class AdminFormSet(BaseModelFormSet):
context = { context = {
'formset': self 'formset': self
} }
return template.render(context) return template.render(Context(context))
class AdminPasswordChangeForm(forms.Form): class AdminPasswordChangeForm(forms.Form):

View File

@ -3,7 +3,7 @@ from copy import deepcopy
from admin_tools.menu import items, Menu from admin_tools.menu import items, Menu
from django.urls import reverse from django.urls import reverse
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.core import services, accounts, administration from orchestra.core import services, accounts, administration
@ -73,7 +73,7 @@ class OrchestraMenu(Menu):
self.children = [ self.children = [
# items.MenuItem( # items.MenuItem(
# mark_safe('{site_name} <span style="{version_style}">v{version}</span>'.format( # mark_safe('{site_name} <span style="{version_style}">v{version}</span>'.format(
# site_name=force_text(settings.SITE_VERBOSE_NAME), # site_name=force_str(settings.SITE_VERBOSE_NAME),
# version_style="text-transform:none; float:none; font-size:smaller; background:none;", # version_style="text-transform:none; float:none; font-size:smaller; background:none;",
# version=get_version())), # version=get_version())),
# reverse('admin:index') # reverse('admin:index')

View File

@ -1,7 +1,7 @@
from urllib import parse from urllib import parse
from django import forms from django import forms
from django.conf.urls import url from django.urls import re_path as url
from django.contrib import admin, messages from django.contrib import admin, messages
from django.contrib.admin.options import IS_POPUP_VAR from django.contrib.admin.options import IS_POPUP_VAR
from django.contrib.admin.utils import unquote from django.contrib.admin.utils import unquote
@ -12,9 +12,9 @@ from django.forms.models import BaseInlineFormSet
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.encoding import force_text from django.utils.encoding import force_str
from django.utils.html import escape from django.utils.html import escape
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views.decorators.debug import sensitive_post_parameters from django.views.decorators.debug import sensitive_post_parameters
from orchestra.models.utils import has_db_field from orchestra.models.utils import has_db_field
@ -230,7 +230,7 @@ class ExtendedModelAdmin(ChangeViewActionsMixin,
if obj is None: if obj is None:
opts = self.model._meta opts = self.model._meta
raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % { raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {
'name': force_text(opts.verbose_name), 'key': escape(object_id)}) 'name': force_str(opts.verbose_name), 'key': escape(object_id)})
return obj return obj

View File

@ -1,8 +1,8 @@
from django.contrib.admin.options import get_content_type_for_model from django.contrib.admin.options import get_content_type_for_model
from django.conf import settings as django_settings from django.conf import settings as django_settings
from django.utils.encoding import force_text from django.utils.encoding import force_str
from django.utils.module_loading import autodiscover_modules from django.utils.module_loading import autodiscover_modules
from django.utils.translation import ugettext as _ from django.utils.translation import gettext as _
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from orchestra import settings from orchestra import settings
@ -52,7 +52,7 @@ class LogApiMixin(object):
user_id=request.user.pk, user_id=request.user.pk,
content_type_id=get_content_type_for_model(instance).pk, content_type_id=get_content_type_for_model(instance).pk,
object_id=instance.pk, object_id=instance.pk,
object_repr=force_text(instance), object_repr=force_str(instance),
action_flag=action, action_flag=action,
change_message=message, change_message=message,
) )

View File

@ -3,7 +3,7 @@ import copy
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.forms import widgets from django.forms import widgets
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from rest_framework.utils import model_meta from rest_framework.utils import model_meta

View File

@ -9,10 +9,10 @@ from django.db import router
from django.shortcuts import redirect, render from django.shortcuts import redirect, render
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import force_text from django.utils.encoding import force_str
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils.translation import ungettext, ugettext_lazy as _ from django.utils.translation import ngettext, gettext_lazy as _
from orchestra.core import services from orchestra.core import services
@ -84,7 +84,7 @@ def delete_related_services(modeladmin, request, queryset):
def format(obj, account=False): def format(obj, account=False):
has_admin = obj.__class__ in admin_site._registry has_admin = obj.__class__ in admin_site._registry
opts = obj._meta opts = obj._meta
no_edit_link = '%s: %s' % (capfirst(opts.verbose_name), force_text(obj)) no_edit_link = '%s: %s' % (capfirst(opts.verbose_name), force_str(obj))
if has_admin: if has_admin:
try: try:
@ -154,7 +154,7 @@ def delete_related_services(modeladmin, request, queryset):
if accounts: if accounts:
relateds = len(to_delete) relateds = len(to_delete)
for obj in to_delete: for obj in to_delete:
obj_display = force_text(obj) obj_display = force_str(obj)
modeladmin.log_deletion(request, obj, obj_display) modeladmin.log_deletion(request, obj, obj_display)
obj.delete() obj.delete()
context = { context = {
@ -167,9 +167,9 @@ def delete_related_services(modeladmin, request, queryset):
return None return None
if len(queryset) == 1: if len(queryset) == 1:
objects_name = force_text(opts.verbose_name) objects_name = force_str(opts.verbose_name)
else: else:
objects_name = force_text(opts.verbose_name_plural) objects_name = force_str(opts.verbose_name_plural)
model_count = {} model_count = {}
for model, objs in collector.model_objs.items(): for model, objs in collector.model_objs.items():
@ -214,7 +214,7 @@ def disable_selected(modeladmin, request, queryset, disable=True):
account.disable() if disable else account.enable() account.disable() if disable else account.enable()
modeladmin.log_change(request, account, verbose_action_name.capitalize()) modeladmin.log_change(request, account, verbose_action_name.capitalize())
n += 1 n += 1
modeladmin.message_user(request, ungettext( modeladmin.message_user(request, ngettext(
_("One account has been successfully %s.") % verbose_action_name, _("One account has been successfully %s.") % verbose_action_name,
_("%i accounts have been successfully %s.") % (n, verbose_action_name), _("%i accounts have been successfully %s.") % (n, verbose_action_name),
n) n)
@ -227,7 +227,7 @@ def disable_selected(modeladmin, request, queryset, disable=True):
def format(obj): def format(obj):
has_admin = obj.__class__ in admin_site._registry has_admin = obj.__class__ in admin_site._registry
opts = obj._meta opts = obj._meta
no_edit_link = '%s: %s' % (capfirst(opts.verbose_name), force_text(obj)) no_edit_link = '%s: %s' % (capfirst(opts.verbose_name), force_str(obj))
if has_admin: if has_admin:
try: try:
admin_url = reverse( admin_url = reverse(
@ -258,9 +258,9 @@ def disable_selected(modeladmin, request, queryset, disable=True):
display.append([format(account), current]) display.append([format(account), current])
if len(queryset) == 1: if len(queryset) == 1:
objects_name = force_text(opts.verbose_name) objects_name = force_str(opts.verbose_name)
else: else:
objects_name = force_text(opts.verbose_name_plural) objects_name = force_str(opts.verbose_name_plural)
context = dict( context = dict(
admin_site.each_context(request), admin_site.each_context(request),

View File

@ -4,7 +4,7 @@ from urllib.parse import parse_qsl
from django import forms from django import forms
from django.apps import apps from django.apps import apps
from django.conf.urls import url from django.urls import re_path as url
from django.contrib import admin, messages from django.contrib import admin, messages
from django.contrib.admin.utils import unquote from django.contrib.admin.utils import unquote
from django.contrib.auth import admin as auth from django.contrib.auth import admin as auth
@ -12,7 +12,7 @@ from django.urls import reverse
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.templatetags.static import static from django.templatetags.static import static
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
from orchestra.admin.actions import SendEmail from orchestra.admin.actions import SendEmail
@ -26,12 +26,13 @@ from .actions import (list_contacts, service_report, delete_related_services, di
enable_selected) enable_selected)
from .forms import AccountCreationForm from .forms import AccountCreationForm
from .models import Account from .models import Account
from .filters import HasTipeServerFilter
class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin): class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin):
list_display = ('username', 'full_name', 'type', 'is_active') list_display = ('username', 'full_name', 'type', 'is_active')
list_filter = ( list_filter = (
'type', 'is_active', 'type', 'is_active', HasTipeServerFilter
) )
add_fieldsets = ( add_fieldsets = (
(_("User"), { (_("User"), {

View File

@ -1,4 +1,4 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import viewsets, exceptions from rest_framework import viewsets, exceptions
from orchestra.api import router, SetPasswordApiMixin, LogApiMixin from orchestra.api import router, SetPasswordApiMixin, LogApiMixin

View File

@ -1,6 +1,6 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.db.models.signals import post_migrate from django.db.models.signals import post_migrate
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.core import services, accounts from orchestra.core import services, accounts

View File

@ -1,7 +1,9 @@
from django.contrib.admin import SimpleListFilter from django.contrib.admin import SimpleListFilter
from django.db.models import Q from django.db.models import Q
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.contrib.orchestration.models import Server
from orchestra.contrib.websites.models import Website
from orchestra.settings import WEB_SERVERS
class IsActiveListFilter(SimpleListFilter): class IsActiveListFilter(SimpleListFilter):
title = _("is active") title = _("is active")
@ -25,3 +27,16 @@ class IsActiveListFilter(SimpleListFilter):
elif self.value() == 'object': elif self.value() == 'object':
return queryset.filter(is_active=False) return queryset.filter(is_active=False)
return queryset return queryset
class HasTipeServerFilter(SimpleListFilter):
title = _("has type server")
parameter_name = 'has_servers'
def lookups(self, request, model_admin):
return [ (x.id, x.name) for x in Server.objects.filter(name__in=WEB_SERVERS) ]
def queryset(self, request, queryset):
if self.value() is not None:
serverWebsites = Website.objects.filter(target_server=self.value())
return queryset.filter(id__in=[ x.account.id for x in serverWebsites ] )
return queryset

View File

@ -4,7 +4,7 @@ from collections import OrderedDict
from django import forms from django import forms
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.apps import apps from django.apps import apps
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.forms import UserCreationForm from orchestra.forms import UserCreationForm

View File

@ -1,45 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import django.core.validators
import django.db.models.deletion
import django.utils.timezone
import django.contrib.auth.models
class Migration(migrations.Migration):
dependencies = [
# Permissions and contenttypes
('auth', '0006_require_contenttypes_0002'),
('systemusers', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Account',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
('password', models.CharField(verbose_name='password', max_length=128)),
('last_login', models.DateTimeField(blank=True, verbose_name='last login', null=True)),
('username', models.CharField(help_text='Required. 64 characters or fewer. Letters, digits and ./-/_ only.', unique=True, validators=[django.core.validators.RegexValidator('^[\\w.-]+$', 'Enter a valid username.', 'invalid')], max_length=32, verbose_name='username')),
('short_name', models.CharField(blank=True, verbose_name='short name', max_length=64)),
('full_name', models.CharField(verbose_name='full name', max_length=256)),
('email', models.EmailField(help_text='Used for password recovery', max_length=254, verbose_name='email address')),
('type', models.CharField(verbose_name='type', choices=[('INDIVIDUAL', 'Individual'), ('ASSOCIATION', 'Association'), ('CUSTOMER', 'Customer'), ('COMPANY', 'Company'), ('PUBLICBODY', 'Public body'), ('STAFF', 'Staff'), ('FRIEND', 'Friend')], max_length=32, default='INDIVIDUAL')),
('language', models.CharField(verbose_name='language', choices=[('EN', 'English')], max_length=2, default='EN')),
('comments', models.TextField(blank=True, verbose_name='comments', max_length=256)),
('is_superuser', models.BooleanField(help_text='Designates that this user has all permissions without explicitly assigning them.', default=False, verbose_name='superuser status')),
('is_active', models.BooleanField(help_text='Designates whether this account should be treated as active. Unselect this instead of deleting accounts.', default=True, verbose_name='active')),
('date_joined', models.DateTimeField(verbose_name='date joined', default=django.utils.timezone.now)),
('main_systemuser', models.ForeignKey(to='systemusers.SystemUser', editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='accounts_main')),
],
options={
'abstract': False,
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
]

View File

@ -1,86 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2021-04-22 11:08
from __future__ import unicode_literals
import django.contrib.auth.models
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
import orchestra.contrib.accounts.models
class Migration(migrations.Migration):
replaces = [('accounts', '0001_initial'), ('accounts', '0002_auto_20170528_2005'), ('accounts', '0003_auto_20210330_1049'), ('accounts', '0004_auto_20210422_1108')]
initial = True
dependencies = [
('systemusers', '0001_initial'),
('auth', '0006_require_contenttypes_0002'),
]
operations = [
migrations.CreateModel(
name='Account',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('username', models.CharField(help_text='Required. 64 characters or fewer. Letters, digits and ./-/_ only.', max_length=32, unique=True, validators=[django.core.validators.RegexValidator('^[\\w.-]+$', 'Enter a valid username.', 'invalid')], verbose_name='username')),
('short_name', models.CharField(blank=True, max_length=64, verbose_name='short name')),
('full_name', models.CharField(max_length=256, verbose_name='full name')),
('email', models.EmailField(help_text='Used for password recovery', max_length=254, verbose_name='email address')),
('type', models.CharField(choices=[('INDIVIDUAL', 'Individual'), ('ASSOCIATION', 'Association'), ('CUSTOMER', 'Customer'), ('COMPANY', 'Company'), ('PUBLICBODY', 'Public body'), ('STAFF', 'Staff'), ('FRIEND', 'Friend')], default='INDIVIDUAL', max_length=32, verbose_name='type')),
('language', models.CharField(choices=[('EN', 'English')], default='EN', max_length=2, verbose_name='language')),
('comments', models.TextField(blank=True, max_length=256, verbose_name='comments')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this account should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('main_systemuser', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='accounts_main', to='systemusers.SystemUser')),
],
options={
'abstract': False,
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
migrations.AlterModelManagers(
name='account',
managers=[
('objects', orchestra.contrib.accounts.models.AccountManager()),
],
),
migrations.AlterField(
model_name='account',
name='language',
field=models.CharField(choices=[('CA', 'Catalan'), ('ES', 'Spanish'), ('EN', 'English')], default='CA', max_length=2, verbose_name='language'),
),
migrations.AlterField(
model_name='account',
name='type',
field=models.CharField(choices=[('INDIVIDUAL', 'Individual'), ('ASSOCIATION', 'Association'), ('CUSTOMER', 'Customer'), ('STAFF', 'Staff'), ('FRIEND', 'Friend')], default='INDIVIDUAL', max_length=32, verbose_name='type'),
),
migrations.AlterField(
model_name='account',
name='username',
field=models.CharField(help_text='Required. 32 characters or fewer. Letters, digits and ./-/_ only.', max_length=32, unique=True, validators=[django.core.validators.RegexValidator('^[\\w.-]+$', 'Enter a valid username.', 'invalid')], verbose_name='username'),
),
migrations.AlterField(
model_name='account',
name='language',
field=models.CharField(choices=[('EN', 'English')], default='EN', max_length=2, verbose_name='language'),
),
migrations.AlterField(
model_name='account',
name='type',
field=models.CharField(choices=[('INDIVIDUAL', 'Individual'), ('ASSOCIATION', 'Association'), ('CUSTOMER', 'Customer'), ('COMPANY', 'Company'), ('PUBLICBODY', 'Public body'), ('STAFF', 'Staff'), ('FRIEND', 'Friend')], default='INDIVIDUAL', max_length=32, verbose_name='type'),
),
migrations.AlterField(
model_name='account',
name='main_systemuser',
field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='accounts_main', to='systemusers.SystemUser'),
),
]

View File

@ -1,38 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-05-28 18:05
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
import orchestra.contrib.accounts.models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0001_initial'),
]
operations = [
migrations.AlterModelManagers(
name='account',
managers=[
('objects', orchestra.contrib.accounts.models.AccountManager()),
],
),
migrations.AlterField(
model_name='account',
name='language',
field=models.CharField(choices=[('CA', 'Catalan'), ('ES', 'Spanish'), ('EN', 'English')], default='CA', max_length=2, verbose_name='language'),
),
migrations.AlterField(
model_name='account',
name='type',
field=models.CharField(choices=[('INDIVIDUAL', 'Individual'), ('ASSOCIATION', 'Association'), ('CUSTOMER', 'Customer'), ('STAFF', 'Staff'), ('FRIEND', 'Friend')], default='INDIVIDUAL', max_length=32, verbose_name='type'),
),
migrations.AlterField(
model_name='account',
name='username',
field=models.CharField(help_text='Required. 32 characters or fewer. Letters, digits and ./-/_ only.', max_length=32, unique=True, validators=[django.core.validators.RegexValidator('^[\\w.-]+$', 'Enter a valid username.', 'invalid')], verbose_name='username'),
),
]

View File

@ -1,25 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2021-03-30 10:49
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0002_auto_20170528_2005'),
]
operations = [
migrations.AlterField(
model_name='account',
name='language',
field=models.CharField(choices=[('EN', 'English')], default='EN', max_length=2, verbose_name='language'),
),
migrations.AlterField(
model_name='account',
name='type',
field=models.CharField(choices=[('INDIVIDUAL', 'Individual'), ('ASSOCIATION', 'Association'), ('CUSTOMER', 'Customer'), ('COMPANY', 'Company'), ('PUBLICBODY', 'Public body'), ('STAFF', 'Staff'), ('FRIEND', 'Friend')], default='INDIVIDUAL', max_length=32, verbose_name='type'),
),
]

View File

@ -5,7 +5,7 @@ from django.db import models
from django.db.models import signals from django.db.models import signals
from django.apps import apps from django.apps import apps
from django.utils import timezone, translation from django.utils import timezone, translation
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
#from orchestra.contrib.orchestration.middlewares import OperationsMiddleware #from orchestra.contrib.orchestration.middlewares import OperationsMiddleware
#from orchestra.contrib.orchestration import Operation #from orchestra.contrib.orchestration import Operation
@ -165,7 +165,6 @@ class Account(auth.AbstractBaseUser):
elif obj and getattr(obj, 'account', None) == self: elif obj and getattr(obj, 'account', None) == self:
return True return True
def has_perms(self, perm_list, obj=None): def has_perms(self, perm_list, obj=None):
""" """
Returns True if the user has each of the specified permissions. If Returns True if the user has each of the specified permissions. If

View File

@ -1,4 +1,4 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.contrib.settings import Setting from orchestra.contrib.settings import Setting
from orchestra.settings import ORCHESTRA_BASE_DOMAIN from orchestra.settings import ORCHESTRA_BASE_DOMAIN

View File

@ -1,5 +1,5 @@
{% extends "orchestra/admin/change_form.html" %} {% extends "orchestra/admin/change_form.html" %}
{% load i18n admin_urls admin_static admin_modify %} {% load i18n admin_urls static admin_modify %}
{% block breadcrumbs %} {% block breadcrumbs %}

View File

@ -12,7 +12,7 @@ from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from django.utils import translation, timezone from django.utils import translation, timezone
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ungettext, ugettext_lazy as _ from django.utils.translation import ngettext, gettext_lazy as _
from orchestra.admin.decorators import action_with_confirmation from orchestra.admin.decorators import action_with_confirmation
from orchestra.admin.forms import AdminFormSet from orchestra.admin.forms import AdminFormSet
@ -69,7 +69,7 @@ def close_bills(modeladmin, request, queryset, action='close_bills'):
'url': url, 'url': url,
'num': num, 'num': num,
} }
message = ungettext( message = ngettext(
_('<a href="%(url)s">One related transaction</a> has been created') % context, _('<a href="%(url)s">One related transaction</a> has been created') % context,
_('<a href="%(url)s">%(num)i related transactions</a> have been created') % context, _('<a href="%(url)s">%(num)i related transactions</a> have been created') % context,
num) num)
@ -111,7 +111,7 @@ def send_bills_action(modeladmin, request, queryset):
bill.send() bill.send()
modeladmin.log_change(request, bill, 'Sent') modeladmin.log_change(request, bill, 'Sent')
num += 1 num += 1
messages.success(request, ungettext( messages.success(request, ngettext(
_("One bill has been sent."), _("One bill has been sent."),
_("%i bills have been sent.") % num, _("%i bills have been sent.") % num,
num)) num))
@ -135,7 +135,7 @@ def download_bills(modeladmin, request, queryset):
pdf = bill.as_pdf() pdf = bill.as_pdf()
archive.writestr('%s.pdf' % bill.number, pdf) archive.writestr('%s.pdf' % bill.number, pdf)
archive.close() archive.close()
response = HttpResponse(bytesio.getvalue(), content_type='application/pdf') response = HttpResponse(bytesio.getvalue(), content_type='application/zip')
response['Content-Disposition'] = 'attachment; filename="orchestra-bills.zip"' response['Content-Disposition'] = 'attachment; filename="orchestra-bills.zip"'
return response return response
bill = queryset[0] bill = queryset[0]
@ -299,7 +299,7 @@ def amend_bills(modeladmin, request, queryset):
'url': amend_url, 'url': amend_url,
'num': num, 'num': num,
} }
messages.success(request, mark_safe(ungettext( messages.success(request, mark_safe(ngettext(
_('<a href="%(url)s">One amendment bill</a> have been generated.') % context, _('<a href="%(url)s">One amendment bill</a> have been generated.') % context,
_('<a href="%(url)s">%(num)i amendment bills</a> have been generated.') % context, _('<a href="%(url)s">%(num)i amendment bills</a> have been generated.') % context,
num num

View File

@ -1,5 +1,5 @@
from django import forms from django import forms
from django.conf.urls import url from django.urls import re_path as url
from django.contrib import admin, messages from django.contrib import admin, messages
from django.contrib.admin.utils import unquote from django.contrib.admin.utils import unquote
from django.urls import reverse from django.urls import reverse
@ -9,7 +9,7 @@ from django.db.models.functions import Coalesce
from django.templatetags.static import static from django.templatetags.static import static
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.shortcuts import redirect from django.shortcuts import redirect
from orchestra.admin import ExtendedModelAdmin from orchestra.admin import ExtendedModelAdmin
@ -126,7 +126,7 @@ class ClosedBillLineInline(BillLineInline):
return line.compute_total() return line.compute_total()
display_total.short_description = _("Total") display_total.short_description = _("Total")
def has_add_permission(self, request): def has_add_permission(self, request, obj):
return False return False

View File

@ -2,7 +2,7 @@ from django.contrib.admin import SimpleListFilter
from django.urls import reverse from django.urls import reverse
from django.db.models import Q from django.db.models import Q
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from . models import Bill from . models import Bill

View File

@ -1,5 +1,5 @@
from django import forms from django import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin.utils import admin_link from orchestra.admin.utils import admin_link
from orchestra.forms import SpanWidget from orchestra.forms import SpanWidget

View File

@ -1,10 +1,10 @@
from django.contrib import messages from django.contrib import messages
from django.urls import reverse from django.urls import reverse
from django.utils.encoding import force_text from django.utils.encoding import force_str
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin.utils import change_url from orchestra.admin.utils import change_url
@ -16,14 +16,14 @@ def validate_contact(request, bill, error=True):
valid = True valid = True
send = messages.error if error else messages.warning send = messages.error if error else messages.warning
if not hasattr(bill.account, 'billcontact'): if not hasattr(bill.account, 'billcontact'):
account = force_text(bill.account) account = force_str(bill.account)
url = reverse('admin:accounts_account_change', args=(bill.account_id,)) url = reverse('admin:accounts_account_change', args=(bill.account_id,))
message = msg.format(relation=_("Related"), account=account, url=url) message = msg.format(relation=_("Related"), account=account, url=url)
send(request, mark_safe(message)) send(request, mark_safe(message))
valid = False valid = False
main = type(bill).account.field.related_model.objects.get_main() main = type(bill).account.field.related_model.objects.get_main()
if not hasattr(main, 'billcontact'): if not hasattr(main, 'billcontact'):
account = force_text(main) account = force_str(main)
url = reverse('admin:accounts_account_change', args=(main.id,)) url = reverse('admin:accounts_account_change', args=(main.id,))
message = msg.format(relation=_("Main"), account=account, url=url) message = msg.format(relation=_("Main"), account=account, url=url)
send(request, mark_safe(message)) send(request, mark_safe(message))

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -8,9 +8,9 @@ from django.db.models import F, Sum
from django.db.models.functions import Coalesce from django.db.models.functions import Coalesce
from django.template import loader from django.template import loader
from django.utils import timezone, translation from django.utils import timezone, translation
from django.utils.encoding import force_text from django.utils.encoding import force_str
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin.utils import change_url from orchestra.admin.utils import change_url
from orchestra.contrib.accounts.models import Account from orchestra.contrib.accounts.models import Account
@ -209,7 +209,7 @@ class Bill(models.Model):
def get_payment_state_display(self): def get_payment_state_display(self):
value = self.payment_state value = self.payment_state
return force_text(dict(self.PAYMENT_STATES).get(value, value)) return force_str(dict(self.PAYMENT_STATES).get(value, value))
def get_current_transaction(self): def get_current_transaction(self):
return self.transactions.exclude_rejected().first() return self.transactions.exclude_rejected().first()

View File

@ -1,6 +1,6 @@
from django import forms from django import forms
from django.contrib import admin from django.contrib import admin
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin import AtLeastOneRequiredInlineFormSet, ExtendedModelAdmin from orchestra.admin import AtLeastOneRequiredInlineFormSet, ExtendedModelAdmin
from orchestra.admin.actions import SendEmail from orchestra.admin.actions import SendEmail

View File

@ -1,5 +1,5 @@
from django.contrib.admin import SimpleListFilter from django.contrib.admin import SimpleListFilter
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from .models import Contact from .models import Contact

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,7 @@
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator from django.core.validators import RegexValidator
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.core import validators from orchestra.core import validators
from orchestra.models.fields import MultiSelectField from orchestra.models.fields import MultiSelectField

View File

@ -1,9 +1,9 @@
from django.conf.urls import url from django.urls import re_path as url
from django.contrib import admin from django.contrib import admin
from django.contrib.auth.admin import UserAdmin from django.contrib.auth.admin import UserAdmin
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
from orchestra.admin.utils import change_url from orchestra.admin.utils import change_url
@ -11,7 +11,7 @@ from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import SelectAccountAdminMixin from orchestra.contrib.accounts.admin import SelectAccountAdminMixin
from .filters import HasUserListFilter, HasDatabaseListFilter from .filters import HasUserListFilter, HasDatabaseListFilter
from .forms import DatabaseCreationForm, DatabaseUserChangeForm, DatabaseUserCreationForm from .forms import DatabaseCreationForm, DatabaseUserChangeForm, DatabaseUserCreationForm, DatabaseForm
from .models import Database, DatabaseUser from .models import Database, DatabaseUser
def save_selected(modeladmin, request, queryset): def save_selected(modeladmin, request, queryset):
@ -20,21 +20,21 @@ def save_selected(modeladmin, request, queryset):
save_selected.short_description = "Re-save selected objects" save_selected.short_description = "Re-save selected objects"
class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
list_display = ('name', 'type', 'display_users', 'account_link') list_display = ('name', 'type', 'target_server', 'display_users', 'account_link')
list_filter = ('type', HasUserListFilter) list_filter = ('type', HasUserListFilter)
search_fields = ('name', 'account__username') search_fields = ('name', 'account__username')
change_readonly_fields = ('name', 'type') change_readonly_fields = ('name', 'type', 'target_server')
extra = 1 extra = 1
fieldsets = ( fieldsets = (
(None, { (None, {
'classes': ('extrapretty',), 'classes': ('extrapretty',),
'fields': ('account_link', 'name', 'type', 'users', 'display_users', 'comments'), 'fields': ('account_link', 'name', 'type', 'users', 'display_users', 'comments', 'target_server'),
}), }),
) )
add_fieldsets = ( add_fieldsets = (
(None, { (None, {
'classes': ('wide',), 'classes': ('wide',),
'fields': ('account_link', 'name', 'type') 'fields': ('account_link', 'name', 'type', 'target_server')
}), }),
(_("Create new user"), { (_("Create new user"), {
'classes': ('wide',), 'classes': ('wide',),
@ -45,11 +45,10 @@ class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
'fields': ('user',) 'fields': ('user',)
}), }),
) )
form = DatabaseForm
add_form = DatabaseCreationForm add_form = DatabaseCreationForm
readonly_fields = ('account_link', 'display_users',) readonly_fields = ('account_link', 'display_users',)
filter_horizontal = ['users'] filter_horizontal = ['users']
filter_by_account_fields = ('users',)
list_prefetch_related = ('users',)
actions = (list_accounts, save_selected) actions = (list_accounts, save_selected)
@mark_safe @mark_safe
@ -71,6 +70,7 @@ class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
username=form.cleaned_data['username'], username=form.cleaned_data['username'],
type=obj.type, type=obj.type,
account_id=obj.account.pk, account_id=obj.account.pk,
target_server=form.cleaned_data['target_server'],
) )
user.set_password(form.cleaned_data["password1"]) user.set_password(form.cleaned_data["password1"])
user.save() user.save()
@ -78,22 +78,22 @@ class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
class DatabaseUserAdmin(SelectAccountAdminMixin, ChangePasswordAdminMixin, ExtendedModelAdmin): class DatabaseUserAdmin(SelectAccountAdminMixin, ChangePasswordAdminMixin, ExtendedModelAdmin):
list_display = ('username', 'type', 'display_databases', 'account_link') list_display = ('username', 'target_server', 'type', 'display_databases', 'account_link')
list_filter = ('type', HasDatabaseListFilter) list_filter = ('type', HasDatabaseListFilter)
search_fields = ('username', 'account__username') search_fields = ('username', 'account__username')
form = DatabaseUserChangeForm form = DatabaseUserChangeForm
add_form = DatabaseUserCreationForm add_form = DatabaseUserCreationForm
change_readonly_fields = ('username', 'type') change_readonly_fields = ('username', 'type', 'target_server')
fieldsets = ( fieldsets = (
(None, { (None, {
'classes': ('extrapretty',), 'classes': ('extrapretty',),
'fields': ('account_link', 'username', 'password', 'type', 'display_databases') 'fields': ('account_link', 'username', 'password', 'type', 'display_databases', 'target_server', 'permision')
}), }),
) )
add_fieldsets = ( add_fieldsets = (
(None, { (None, {
'classes': ('extrapretty',), 'classes': ('extrapretty',),
'fields': ('account_link', 'username', 'password1', 'password2', 'type') 'fields': ('account_link', 'username', 'password1', 'password2', 'type', 'target_server', 'permision')
}), }),
) )
readonly_fields = ('account_link', 'display_databases',) readonly_fields = ('account_link', 'display_databases',)

View File

@ -1,5 +1,5 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.core import services from orchestra.core import services

View File

@ -1,6 +1,6 @@
import textwrap import textwrap
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.contrib.orchestration import ServiceController, replace from orchestra.contrib.orchestration import ServiceController, replace
from orchestra.contrib.resources import ServiceMonitor from orchestra.contrib.resources import ServiceMonitor
@ -36,6 +36,12 @@ class MySQLController(ServiceController):
'username': user.username, 'username': user.username,
'grant': 'WITH GRANT OPTION' if user == context['owner'] else '' 'grant': 'WITH GRANT OPTION' if user == context['owner'] else ''
}) })
if user.permision == "ro":
self.append(textwrap.dedent("""\
mysql -e 'GRANT SELECT ON `%(database)s`.* TO "%(username)s"@"%(host)s" %(grant)s;'\
""") % context
)
else:
self.append(textwrap.dedent("""\ self.append(textwrap.dedent("""\
mysql -e 'GRANT ALL PRIVILEGES ON `%(database)s`.* TO "%(username)s"@"%(host)s" %(grant)s;'\ mysql -e 'GRANT ALL PRIVILEGES ON `%(database)s`.* TO "%(username)s"@"%(host)s" %(grant)s;'\
""") % context """) % context
@ -85,8 +91,8 @@ class MySQLUserController(ServiceController):
context = self.get_context(user) context = self.get_context(user)
self.append(textwrap.dedent("""\ self.append(textwrap.dedent("""\
# Create user %(username)s # Create user %(username)s
mysql -e 'CREATE USER "%(username)s"@"%(host)s";' || true # User already exists mysql -e 'CREATE USER IF NOT EXISTS "%(username)s"@"%(host)s";'
mysql -e 'UPDATE mysql.user SET Password="%(password)s" WHERE User="%(username)s";'\ mysql -e 'ALTER USER IF EXISTS "%(username)s"@"%(host)s" IDENTIFIED BY PASSWORD "%(password)s";'\
""") % context """) % context
) )
@ -172,7 +178,7 @@ class MysqlDisk(ServiceMonitor):
def get_context(self, db): def get_context(self, db):
context = { context = {
'db_name': db.name, 'db_name': db.name,
'db_dirname': db.name.replace('-', '@003f'), 'db_dirname': db.name.replace('-', '@002d'),
'db_id': db.pk, 'db_id': db.pk,
'db_type': db.type, 'db_type': db.type,
} }

View File

@ -1,5 +1,5 @@
from django.contrib.admin import SimpleListFilter from django.contrib.admin import SimpleListFilter
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
class HasUserListFilter(SimpleListFilter): class HasUserListFilter(SimpleListFilter):

View File

@ -3,7 +3,7 @@ from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.core import validators from orchestra.core import validators
@ -31,13 +31,39 @@ class DatabaseUserCreationForm(forms.ModelForm):
return password2 return password2
class DatabaseForm(forms.ModelForm):
class Meta:
model = Database
fields = ('name', 'users', 'type', 'account', 'target_server')
def __init__(self, *args, **kwargs):
super(DatabaseForm, self).__init__(*args, **kwargs)
# muestra solo los usuarios del mismo server
account_id = self.instance.account_id
database_server_id = self.instance.target_server_id
if account_id:
self.fields['users'].queryset = DatabaseUser.objects.filter(account=account_id, target_server=database_server_id)
def clean(self):
# verifica que los usuarios petenecen al servidor de la bbdd
database_server_id = self.instance.target_server_id
users = self.cleaned_data.get('users')
if users and database_server_id:
for user in users:
if user.target_server_id != database_server_id:
self.add_error("users", _(f"{user.username} does not belong to the database server"))
return self.cleaned_data
class DatabaseCreationForm(DatabaseUserCreationForm): class DatabaseCreationForm(DatabaseUserCreationForm):
username = forms.CharField(label=_("Username"), max_length=16, username = forms.CharField(label=_("Username"), max_length=32,
required=False, validators=[validators.validate_name], required=False, validators=[validators.validate_name],
help_text=_("Required. 16 characters or fewer. Letters, digits and " help_text=_("Required. 32 characters or fewer. Letters, digits and "
"@/./+/-/_ only."), "@/./+/-/_ only."),
error_messages={ error_messages={
'invalid': _("This value may contain 16 characters or fewer, only letters, numbers and " 'invalid': _("This value may contain 32 characters or fewer, only letters, numbers and "
"@/./+/-/_ characters.")}) "@/./+/-/_ characters.")})
user = forms.ModelChoiceField(required=False, queryset=DatabaseUser.objects) user = forms.ModelChoiceField(required=False, queryset=DatabaseUser.objects)
@ -50,13 +76,14 @@ class DatabaseCreationForm(DatabaseUserCreationForm):
account_id = self.initial.get('account', self.initial_account) account_id = self.initial.get('account', self.initial_account)
if account_id: if account_id:
qs = self.fields['user'].queryset.filter(account=account_id).order_by('username') qs = self.fields['user'].queryset.filter(account=account_id).order_by('username')
choices = [ (u.pk, "%s (%s)" % (u, u.get_type_display())) for u in qs ] choices = [ (u.pk, "%s (%s) (%s)" % (u, u.get_type_display(), str(u.target_server.name) )) for u in qs ]
self.fields['user'].queryset = qs self.fields['user'].queryset = qs
self.fields['user'].choices = [(None, '--------'),] + choices self.fields['user'].choices = [(None, '--------'),] + choices
def clean_username(self): def clean_username(self):
username = self.cleaned_data.get('username') username = self.cleaned_data.get('username')
if DatabaseUser.objects.filter(username=username).exists(): server = self.cleaned_data.get('target_server')
if DatabaseUser.objects.filter(username=username, target_server=server).exists():
raise ValidationError("Provided username already exists.") raise ValidationError("Provided username already exists.")
return username return username
@ -76,6 +103,9 @@ class DatabaseCreationForm(DatabaseUserCreationForm):
if user and user.type != self.cleaned_data.get('type'): if user and user.type != self.cleaned_data.get('type'):
msg = _("Database type and user type doesn't match") msg = _("Database type and user type doesn't match")
raise ValidationError(msg) raise ValidationError(msg)
if user and user.target_server != self.cleaned_data.get('target_server'):
msg = _("Database server and user server doesn't match")
raise ValidationError(msg)
return user return user
def clean(self): def clean(self):

View File

@ -1,52 +1,46 @@
# -*- coding: utf-8 -*- # Generated by Django 2.2.28 on 2023-06-28 17:06
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import orchestra.core.validators import orchestra.core.validators
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
] ]
operations = [ operations = [
migrations.CreateModel(
name='Database',
fields=[
('id', models.AutoField(serialize=False, primary_key=True, verbose_name='ID', auto_created=True)),
('name', models.CharField(verbose_name='name', max_length=64, validators=[orchestra.core.validators.validate_name])),
('type', models.CharField(default='mysql', choices=[('mysql', 'MySQL'), ('postgres', 'PostgreSQL')], verbose_name='type', max_length=32)),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='databases', verbose_name='Account', to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel( migrations.CreateModel(
name='DatabaseUser', name='DatabaseUser',
fields=[ fields=[
('id', models.AutoField(serialize=False, primary_key=True, verbose_name='ID', auto_created=True)), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('username', models.CharField(verbose_name='username', max_length=16, validators=[orchestra.core.validators.validate_name])), ('username', models.CharField(max_length=16, validators=[orchestra.core.validators.validate_name], verbose_name='username')),
('password', models.CharField(verbose_name='password', max_length=256)), ('password', models.CharField(max_length=256, verbose_name='password')),
('type', models.CharField(default='mysql', choices=[('mysql', 'MySQL'), ('postgres', 'PostgreSQL')], verbose_name='type', max_length=32)), ('type', models.CharField(choices=[('mysql', 'MySQL')], default='mysql', max_length=32, verbose_name='type')),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='databaseusers', verbose_name='Account', to=settings.AUTH_USER_MODEL)), ('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='databaseusers', to=settings.AUTH_USER_MODEL, verbose_name='Account')),
], ],
options={ options={
'verbose_name_plural': 'DB users', 'verbose_name_plural': 'DB users',
'unique_together': {('username', 'type')},
}, },
), ),
migrations.AddField( migrations.CreateModel(
model_name='database', name='Database',
name='users', fields=[
field=models.ManyToManyField(related_name='databases', to='databases.DatabaseUser', verbose_name='users', blank=True), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
), ('name', models.CharField(max_length=64, validators=[orchestra.core.validators.validate_name], verbose_name='name')),
migrations.AlterUniqueTogether( ('type', models.CharField(choices=[('mysql', 'MySQL')], default='mysql', max_length=32, verbose_name='type')),
name='databaseuser', ('comments', models.TextField(blank=True, default='')),
unique_together=set([('username', 'type')]), ('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='databases', to=settings.AUTH_USER_MODEL, verbose_name='Account')),
), ('users', models.ManyToManyField(blank=True, related_name='databases', to='databases.DatabaseUser', verbose_name='users')),
migrations.AlterUniqueTogether( ],
name='database', options={
unique_together=set([('name', 'type')]), 'unique_together': {('name', 'type')},
},
), ),
] ]

View File

@ -1,82 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2021-04-22 11:25
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import orchestra.core.validators
class Migration(migrations.Migration):
replaces = [('databases', '0001_initial'), ('databases', '0002_auto_20170528_2005'), ('databases', '0003_database_comments'), ('databases', '0004_auto_20210330_1049')]
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Database',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=64, validators=[orchestra.core.validators.validate_name], verbose_name='name')),
('type', models.CharField(choices=[('mysql', 'MySQL'), ('postgres', 'PostgreSQL')], default='mysql', max_length=32, verbose_name='type')),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='databases', to=settings.AUTH_USER_MODEL, verbose_name='Account')),
],
),
migrations.CreateModel(
name='DatabaseUser',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('username', models.CharField(max_length=16, validators=[orchestra.core.validators.validate_name], verbose_name='username')),
('password', models.CharField(max_length=256, verbose_name='password')),
('type', models.CharField(choices=[('mysql', 'MySQL'), ('postgres', 'PostgreSQL')], default='mysql', max_length=32, verbose_name='type')),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='databaseusers', to=settings.AUTH_USER_MODEL, verbose_name='Account')),
],
options={
'verbose_name_plural': 'DB users',
},
),
migrations.AddField(
model_name='database',
name='users',
field=models.ManyToManyField(blank=True, related_name='databases', to='databases.DatabaseUser', verbose_name='users'),
),
migrations.AlterUniqueTogether(
name='databaseuser',
unique_together=set([('username', 'type')]),
),
migrations.AlterUniqueTogether(
name='database',
unique_together=set([('name', 'type')]),
),
migrations.AlterField(
model_name='database',
name='type',
field=models.CharField(choices=[('mysql', 'MySQL')], default='mysql', max_length=32, verbose_name='type'),
),
migrations.AlterField(
model_name='databaseuser',
name='type',
field=models.CharField(choices=[('mysql', 'MySQL')], default='mysql', max_length=32, verbose_name='type'),
),
migrations.AddField(
model_name='database',
name='comments',
field=models.TextField(blank=True, default=''),
),
migrations.AlterField(
model_name='database',
name='type',
field=models.CharField(choices=[('mysql', 'MySQL'), ('postgres', 'PostgreSQL')], default='mysql', max_length=32, verbose_name='type'),
),
migrations.AlterField(
model_name='databaseuser',
name='type',
field=models.CharField(choices=[('mysql', 'MySQL'), ('postgres', 'PostgreSQL')], default='mysql', max_length=32, verbose_name='type'),
),
]

View File

@ -1,25 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-05-28 18:05
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('databases', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='database',
name='type',
field=models.CharField(choices=[('mysql', 'MySQL')], default='mysql', max_length=32, verbose_name='type'),
),
migrations.AlterField(
model_name='databaseuser',
name='type',
field=models.CharField(choices=[('mysql', 'MySQL')], default='mysql', max_length=32, verbose_name='type'),
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 2.2.28 on 2023-06-28 17:11
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('orchestration', '__first__'),
('databases', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='databaseuser',
name='target_server',
field=models.ForeignKey(default=3, on_delete=django.db.models.deletion.CASCADE, to='orchestration.Server', verbose_name='Target Server'),
),
]

View File

@ -0,0 +1,29 @@
# Generated by Django 2.2.28 on 2023-06-29 16:38
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('orchestration', '__first__'),
('databases', '0002_databaseuser_target_server'),
]
operations = [
migrations.AddField(
model_name='databaseuser',
name='permision',
field=models.CharField(choices=[('all', 'all'), ('ro', 'read only')], default='all', max_length=20, verbose_name='Permisson'),
),
migrations.AlterField(
model_name='databaseuser',
name='target_server',
field=models.ForeignKey(default=3, on_delete=django.db.models.deletion.CASCADE, to='orchestration.Server', verbose_name='Server'),
),
migrations.AlterUniqueTogether(
name='databaseuser',
unique_together={('username', 'type', 'target_server')},
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2020-02-04 11:21
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('databases', '0002_auto_20170528_2005'),
]
operations = [
migrations.AddField(
model_name='database',
name='comments',
field=models.TextField(default=''),
),
]

View File

@ -1,30 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2021-03-30 10:49
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('databases', '0003_database_comments'),
]
operations = [
migrations.AlterField(
model_name='database',
name='comments',
field=models.TextField(blank=True, default=''),
),
migrations.AlterField(
model_name='database',
name='type',
field=models.CharField(choices=[('mysql', 'MySQL'), ('postgres', 'PostgreSQL')], default='mysql', max_length=32, verbose_name='type'),
),
migrations.AlterField(
model_name='databaseuser',
name='type',
field=models.CharField(choices=[('mysql', 'MySQL'), ('postgres', 'PostgreSQL')], default='mysql', max_length=32, verbose_name='type'),
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 2.2.28 on 2023-06-29 16:57
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('orchestration', '__first__'),
('databases', '0003_auto_20230629_1838'),
]
operations = [
migrations.AddField(
model_name='database',
name='target_server',
field=models.ForeignKey(default=3, on_delete=django.db.models.deletion.CASCADE, to='orchestration.Server', verbose_name='Server'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.28 on 2023-07-05 10:08
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('orchestration', '__first__'),
('databases', '0004_database_target_server'),
]
operations = [
migrations.AlterUniqueTogether(
name='database',
unique_together={('name', 'type', 'target_server')},
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 2.2.28 on 2023-07-05 10:37
from django.db import migrations, models
import orchestra.core.validators
class Migration(migrations.Migration):
dependencies = [
('databases', '0005_auto_20230705_1208'),
]
operations = [
migrations.AlterField(
model_name='databaseuser',
name='username',
field=models.CharField(max_length=32, validators=[orchestra.core.validators.validate_name], verbose_name='username'),
),
]

View File

@ -1,7 +1,7 @@
import hashlib import hashlib
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.core import validators from orchestra.core import validators
@ -23,9 +23,11 @@ class Database(models.Model):
account = models.ForeignKey('accounts.Account', on_delete=models.CASCADE, account = models.ForeignKey('accounts.Account', on_delete=models.CASCADE,
verbose_name=_("Account"), related_name='databases') verbose_name=_("Account"), related_name='databases')
comments = models.TextField(default="", blank=True) comments = models.TextField(default="", blank=True)
target_server = models.ForeignKey('orchestration.Server', on_delete=models.CASCADE,
verbose_name=_("Server"), default=3 )
class Meta: class Meta:
unique_together = ('name', 'type') unique_together = ('name', 'type', 'target_server')
def __str__(self): def __str__(self):
return "%s" % self.name return "%s" % self.name
@ -54,7 +56,12 @@ class DatabaseUser(models.Model):
MYSQL = Database.MYSQL MYSQL = Database.MYSQL
POSTGRESQL = Database.POSTGRESQL POSTGRESQL = Database.POSTGRESQL
username = models.CharField(_("username"), max_length=16, # MySQL usernames 16 char long typeOfPermision = [
('all','all'),
('ro', 'read only'),
]
username = models.CharField(_("username"), max_length=32, # MySQL usernames 16 char long
validators=[validators.validate_name]) validators=[validators.validate_name])
password = models.CharField(_("password"), max_length=256) password = models.CharField(_("password"), max_length=256)
type = models.CharField(_("type"), max_length=32, type = models.CharField(_("type"), max_length=32,
@ -62,10 +69,14 @@ class DatabaseUser(models.Model):
default=settings.DATABASES_DEFAULT_TYPE) default=settings.DATABASES_DEFAULT_TYPE)
account = models.ForeignKey('accounts.Account', on_delete=models.CASCADE, account = models.ForeignKey('accounts.Account', on_delete=models.CASCADE,
verbose_name=_("Account"), related_name='databaseusers') verbose_name=_("Account"), related_name='databaseusers')
target_server = models.ForeignKey('orchestration.Server', on_delete=models.CASCADE,
verbose_name=_("Server"), default=3 )
permision = models.CharField(verbose_name=_("Permisson"), max_length=20, choices=typeOfPermision, default='all')
class Meta: class Meta:
verbose_name_plural = _("DB users") verbose_name_plural = _("DB users")
unique_together = ('username', 'type') unique_together = ('username', 'type', 'target_server')
def __str__(self): def __str__(self):
return self.username return self.username

View File

@ -7,7 +7,7 @@ from django.db.models.functions import Concat, Coalesce
from django.forms.models import modelformset_factory from django.forms.models import modelformset_factory
from django.shortcuts import render from django.shortcuts import render
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ungettext, ugettext_lazy as _ from django.utils.translation import ngettext, gettext_lazy as _
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from orchestra.admin.utils import get_object_from_url, change_url, admin_link from orchestra.admin.utils import get_object_from_url, change_url, admin_link
@ -84,7 +84,7 @@ def edit_records(modeladmin, request, queryset):
change_message = modeladmin.construct_change_message(request, fake_form, [formset]) change_message = modeladmin.construct_change_message(request, fake_form, [formset])
modeladmin.log_change(request, formset.instance, change_message) modeladmin.log_change(request, formset.instance, change_message)
num = len(formsets) num = len(formsets)
message = ungettext( message = ngettext(
_("Records for one selected domain have been updated."), _("Records for one selected domain have been updated."),
_("Records for %i selected domains have been updated.") % num, _("Records for %i selected domains have been updated.") % num,
num) num)
@ -127,7 +127,7 @@ def set_soa(modeladmin, request, queryset):
modeladmin.log_change(request, domain, change_message) modeladmin.log_change(request, domain, change_message)
domain.save() domain.save()
num = len(queryset) num = len(queryset)
msg = ungettext( msg = ngettext(
_("SOA record for one domain has been updated."), _("SOA record for one domain has been updated."),
_("SOA record for %s domains has been updated.") % num, _("SOA record for %s domains has been updated.") % num,
num num

View File

@ -5,7 +5,7 @@ from django.db.models.functions import Concat, Coalesce
from django.templatetags.static import static from django.templatetags.static import static
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext, ugettext_lazy as _ from django.utils.translation import gettext, gettext_lazy as _
from orchestra.admin import ExtendedModelAdmin from orchestra.admin import ExtendedModelAdmin
from orchestra.admin.utils import admin_link, change_url from orchestra.admin.utils import admin_link, change_url

View File

@ -2,7 +2,7 @@ import re
import socket import socket
import textwrap import textwrap
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from orchestra.contrib.orchestration import ServiceController from orchestra.contrib.orchestration import ServiceController
from orchestra.contrib.orchestration import Operation from orchestra.contrib.orchestration import Operation

Some files were not shown because too many files have changed in this diff Show More