Random fixes
This commit is contained in:
parent
e4edb89d2c
commit
38a46b5983
3
TODO.md
3
TODO.md
|
@ -423,4 +423,5 @@ mkhomedir_helper or create ssh homes with bash.rc and such
|
|||
# account contacts inline, show provided fields and ignore the rest?
|
||||
# email usage -webkit-column-count:3;-moz-column-count:3;column-count:3;
|
||||
|
||||
# Protect fucking search url and put into /admin/search and admin.py
|
||||
|
||||
# wordpressmu custom_url: set blog.domain
|
||||
|
|
|
@ -39,24 +39,56 @@ def get_urls():
|
|||
admin.site.get_urls = get_urls
|
||||
|
||||
|
||||
def get_model(model_name, model_name_map):
|
||||
try:
|
||||
return model_name_map[model_name.lower()]
|
||||
except KeyError:
|
||||
return
|
||||
|
||||
|
||||
def search(request):
|
||||
query = request.GET.get('q', '')
|
||||
if query.endswith('!'):
|
||||
search_term = query
|
||||
models = set()
|
||||
selected_models = set()
|
||||
model_name_map = {}
|
||||
for service in itertools.chain(services, accounts):
|
||||
if service.search:
|
||||
models.add(service.model)
|
||||
model_name_map[service.model._meta.model_name] = service.model
|
||||
|
||||
# Account direct access
|
||||
if search_term.endswith('!'):
|
||||
from ..contrib.accounts.models import Account
|
||||
# Account direct access
|
||||
query = query.replace('!', '')
|
||||
search_term = search_term.replace('!', '')
|
||||
try:
|
||||
account = Account.objects.get(username=query)
|
||||
account = Account.objects.get(username=search_term)
|
||||
except Account.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
account_url = reverse('admin:accounts_account_change', args=(account.pk,))
|
||||
return redirect(account_url)
|
||||
# Search for specific model
|
||||
elif ':' in search_term:
|
||||
new_search_term = []
|
||||
for part in search_term.split():
|
||||
if ':' in part:
|
||||
model_name, term = part.split(':')
|
||||
model = get_model(model_name, model_name_map)
|
||||
# Retry with singular version
|
||||
if model is None and model_name.endswith('s'):
|
||||
model = get_model(model_name[:-1], model_name_map)
|
||||
if model is None:
|
||||
new_search_term.append(':'.join((model_name, term)))
|
||||
else:
|
||||
selected_models.add(model)
|
||||
new_search_term.append(term)
|
||||
else:
|
||||
new_search_term.append(part)
|
||||
search_term = ' '.join(new_search_term)
|
||||
if selected_models:
|
||||
models = selected_models
|
||||
results = OrderedDict()
|
||||
models = set()
|
||||
for service in itertools.chain(services, accounts):
|
||||
if service.search:
|
||||
models.add(service.model)
|
||||
models = sorted(models, key=lambda m: m._meta.verbose_name_plural.lower())
|
||||
total = 0
|
||||
for model in models:
|
||||
|
@ -66,7 +98,7 @@ def search(request):
|
|||
pass
|
||||
else:
|
||||
qs = modeladmin.get_queryset(request)
|
||||
qs, search_use_distinct = modeladmin.get_search_results(request, qs, query)
|
||||
qs, search_use_distinct = modeladmin.get_search_results(request, qs, search_term)
|
||||
if search_use_distinct:
|
||||
qs = qs.distinct()
|
||||
num = len(qs)
|
||||
|
@ -79,6 +111,7 @@ def search(request):
|
|||
'total': total,
|
||||
'columns': min(int(total/17), 3),
|
||||
'query': query,
|
||||
'search_term': search_term,
|
||||
'results': results,
|
||||
'search_autofocus': True,
|
||||
}
|
||||
|
|
|
@ -80,17 +80,24 @@ class EnhaceSearchMixin(object):
|
|||
def get_search_results(self, request, queryset, search_term):
|
||||
""" allows to specify field <field_name>:<search_term> """
|
||||
search_fields = self.get_search_fields(request)
|
||||
if ':' in search_term:
|
||||
if '=' in search_term:
|
||||
fields = {field.split('__')[0]: field for field in search_fields}
|
||||
new_search_term = []
|
||||
for part in search_term.split():
|
||||
cur_search_term = ''
|
||||
for field, term in pairwise(part.split(':')):
|
||||
field = None
|
||||
if '=' in part:
|
||||
field, term = part.split('=')
|
||||
kwarg = '%s__icontains'
|
||||
c_term = term
|
||||
if term.startswith(('"', "'")) and term.endswith(('"', "'")):
|
||||
c_term = term[1:-1]
|
||||
kwarg = '%s__iexact'
|
||||
if field in fields:
|
||||
queryset = queryset.filter(**{'%s__icontains' % fields[field]: term})
|
||||
queryset = queryset.filter(**{kwarg % fields[field]: c_term})
|
||||
else:
|
||||
cur_search_term += ':'.join((field, term))
|
||||
new_search_term.append(cur_search_term)
|
||||
new_search_term.append('='.join((field, term)))
|
||||
else:
|
||||
new_search_term.append(part)
|
||||
search_term = ' '.join(new_search_term)
|
||||
return super(EnhaceSearchMixin, self).get_search_results(request, queryset, search_term)
|
||||
|
||||
|
|
Binary file not shown.
|
@ -538,7 +538,7 @@ msgstr ""
|
|||
|
||||
#: templates/bills/microspective.html:149
|
||||
msgid "QUESTIONS"
|
||||
msgstr ""
|
||||
msgstr "PREGUNTAS"
|
||||
|
||||
#: templates/bills/microspective.html:150
|
||||
#, python-format
|
||||
|
|
|
@ -330,7 +330,7 @@ class Bill(models.Model):
|
|||
subtotals[tax] = total
|
||||
result = {}
|
||||
for tax, subtotal in subtotals.items():
|
||||
result[tax] = (subtotal, round(tax/100*subtotal, 2))
|
||||
result[tax] = [subtotal, round(tax/100*subtotal, 2)]
|
||||
return result
|
||||
|
||||
@lru_cache()
|
||||
|
|
|
@ -127,13 +127,30 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo
|
|||
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
|
||||
if not add:
|
||||
self.check_unrelated_address(request, obj)
|
||||
self.check_matching_address(request, obj)
|
||||
return super(MailboxAdmin, self).render_change_form(
|
||||
request, context, add, change, form_url, obj)
|
||||
|
||||
def log_addition(self, request, object):
|
||||
self.check_unrelated_address(request, object)
|
||||
self.check_matching_address(request, object)
|
||||
return super(MailboxAdmin, self).log_addition(request, object)
|
||||
|
||||
def check_matching_address(self, request, obj):
|
||||
local_domain = settings.MAILBOXES_LOCAL_DOMAIN
|
||||
if obj.name and local_domain:
|
||||
try:
|
||||
addr = Address.objects.get(
|
||||
name=obj.name, domain__name=local_domain, account_id=self.account.pk)
|
||||
except Address.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
if addr not in obj.addresses.all():
|
||||
msg = _("Mailbox '%s' local address matches '%s', please consider if "
|
||||
"selecting it makes sense.") % (obj, addr)
|
||||
if msg not in (m.message for m in messages.get_messages(request)):
|
||||
self.message_user(request, msg, level=messages.WARNING)
|
||||
|
||||
def check_unrelated_address(self, request, obj):
|
||||
# Check if there exists an unrelated local Address for this mbox
|
||||
local_domain = settings.MAILBOXES_LOCAL_DOMAIN
|
||||
|
@ -169,7 +186,9 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
|||
fields = ('account_link', 'email_link', 'mailboxes', 'forward')
|
||||
add_fields = ('account_link', ('name', 'domain'), 'mailboxes', 'forward')
|
||||
# inlines = [AutoresponseInline]
|
||||
search_fields = ('forward', 'mailboxes__name', 'account__username', 'computed_email')
|
||||
search_fields = (
|
||||
'forward', 'mailboxes__name', 'account__username', 'computed_email', 'domain__name'
|
||||
)
|
||||
readonly_fields = ('account_link', 'domain_link', 'email_link')
|
||||
actions = (SendAddressEmail(),)
|
||||
filter_by_account_fields = ('domain', 'mailboxes')
|
||||
|
@ -224,6 +243,29 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
|||
qs = super(AddressAdmin, self).get_queryset(request)
|
||||
return qs.annotate(computed_email=Concat(F('name'), V('@'), F('domain__name')))
|
||||
|
||||
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
|
||||
if not add:
|
||||
self.check_matching_mailbox(request, obj)
|
||||
return super(AddressAdmin, self).render_change_form(
|
||||
request, context, add, change, form_url, obj)
|
||||
|
||||
def log_addition(self, request, object):
|
||||
self.check_matching_mailbox(request, object)
|
||||
return super(AddressAdmin, self).log_addition(request, object)
|
||||
|
||||
def check_matching_mailbox(self, request, obj):
|
||||
# Check if new addresse matches with a mbox because of having a local domain
|
||||
if obj.name and obj.domain and obj.domain.name == settings.MAILBOXES_LOCAL_DOMAIN:
|
||||
if obj.name not in obj.forward.split() and Mailbox.objects.filter(name=obj.name).exists():
|
||||
for mailbox in obj.mailboxes.all():
|
||||
if mailbox.name == obj.name:
|
||||
return
|
||||
msg = _("Address '%s' matches mailbox '%s' local address, please consider "
|
||||
"if makes sense adding the mailbox on the mailboxes or forward field."
|
||||
) % (obj, obj.name)
|
||||
if msg not in (m.message for m in messages.get_messages(request)):
|
||||
self.message_user(request, msg, level=messages.WARNING)
|
||||
|
||||
|
||||
admin.site.register(Mailbox, MailboxAdmin)
|
||||
admin.site.register(Address, AddressAdmin)
|
||||
|
|
|
@ -45,24 +45,6 @@ class MailboxForm(forms.ModelForm):
|
|||
if self.instance and self.instance.pk:
|
||||
self.fields['addresses'].initial = self.instance.addresses.all()
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(MailboxForm, self).clean()
|
||||
name = self.instance.name if self.instance.pk else cleaned_data.get('name')
|
||||
local_domain = settings.MAILBOXES_LOCAL_DOMAIN
|
||||
if name and local_domain:
|
||||
try:
|
||||
addr = Address.objects.get(
|
||||
name=name, domain__name=local_domain, account_id=self.modeladmin.account.pk)
|
||||
except Address.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
if addr not in cleaned_data.get('addresses', []):
|
||||
raise ValidationError({
|
||||
'addresses': _("This mailbox local address matche '%s', "
|
||||
"please make explicit this fact by selecting it.") % addr
|
||||
})
|
||||
return cleaned_data
|
||||
|
||||
|
||||
class MailboxChangeForm(UserChangeForm, MailboxForm):
|
||||
pass
|
||||
|
@ -86,20 +68,3 @@ class AddressForm(forms.ModelForm):
|
|||
forward = cleaned_data.get('forward', '')
|
||||
if not cleaned_data.get('mailboxes', True) and not forward:
|
||||
raise ValidationError(_("Mailboxes or forward address should be provided."))
|
||||
# Check if new addresse matches with a mbox because of having a local domain
|
||||
if self.instance.pk:
|
||||
name = self.instance.name
|
||||
domain = self.instance.domain
|
||||
else:
|
||||
name = cleaned_data.get('name')
|
||||
domain = cleaned_data.get('domain')
|
||||
if domain and name and domain.name == settings.MAILBOXES_LOCAL_DOMAIN:
|
||||
if name not in forward.split() and Mailbox.objects.filter(name=name).exists():
|
||||
for mailbox in cleaned_data.get('mailboxes', []):
|
||||
if mailbox.name == name:
|
||||
return
|
||||
raise ValidationError(
|
||||
_("This address matches mailbox '%s' local address, please make explicit "
|
||||
"this fact by adding the mailbox on the mailboxes or forward field.") % name
|
||||
)
|
||||
return cleaned_data
|
||||
|
|
|
@ -103,7 +103,7 @@ def OpenSSH(backend, log, server, cmds, async=False):
|
|||
script = '\n'.join(cmds)
|
||||
script = script.replace('\r', '')
|
||||
log.state = log.STARTED
|
||||
log.script = script
|
||||
log.script = '\n'.join((log.script, script))
|
||||
log.save(update_fields=('script', 'state', 'updated_at'))
|
||||
if not cmds:
|
||||
return
|
||||
|
@ -116,12 +116,14 @@ def OpenSSH(backend, log, server, cmds, async=False):
|
|||
log.stdout += state.stdout.decode('utf8')
|
||||
log.stderr += state.stderr.decode('utf8')
|
||||
log.save(update_fields=('stdout', 'stderr', 'updated_at'))
|
||||
log.exit_code = state.exit_code
|
||||
exit_code = state.exit_code
|
||||
else:
|
||||
log.stdout = ssh.stdout.decode('utf8')
|
||||
log.stderr = ssh.stderr.decode('utf8')
|
||||
log.exit_code = ssh.exit_code
|
||||
log.state = log.SUCCESS if log.exit_code == 0 else log.FAILURE
|
||||
log.stdout += ssh.stdout.decode('utf8')
|
||||
log.stderr += ssh.stderr.decode('utf8')
|
||||
exit_code = ssh.exit_code
|
||||
if not log.exit_code:
|
||||
log.exit_code = exit_code
|
||||
log.state = log.SUCCESS if exit_code == 0 else log.FAILURE
|
||||
logger.debug('%s execution state on %s is %s' % (backend, server, log.state))
|
||||
log.save()
|
||||
except:
|
||||
|
@ -164,11 +166,12 @@ def Python(backend, log, server, cmds, async=False):
|
|||
except:
|
||||
log.exit_code = 1
|
||||
log.state = log.FAILURE
|
||||
log.stdout = '\n'.join(stdout)
|
||||
log.traceback = ExceptionInfo(sys.exc_info()).traceback
|
||||
log.stdout += '\n'.join(stdout)
|
||||
log.traceback += ExceptionInfo(sys.exc_info()).traceback
|
||||
logger.error('Exception while executing %s on %s' % (backend, server))
|
||||
else:
|
||||
log.exit_code = 0
|
||||
log.state = log.SUCCESS
|
||||
if not log.exit_code:
|
||||
log.exit_code = 0
|
||||
log.state = log.SUCCESS
|
||||
logger.debug('%s execution state on %s is %s' % (backend, server, log.state))
|
||||
log.save()
|
||||
|
|
Binary file not shown.
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-07-15 12:08+0000\n"
|
||||
"POT-Creation-Date: 2015-10-08 11:53+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -52,7 +52,7 @@ msgstr ""
|
|||
msgid "Process"
|
||||
msgstr ""
|
||||
|
||||
#: actions.py:63 actions.py:134 models.py:97 models.py:164
|
||||
#: actions.py:63 actions.py:134 models.py:97 models.py:172
|
||||
msgid "Executed"
|
||||
msgstr ""
|
||||
|
||||
|
@ -112,7 +112,7 @@ msgstr ""
|
|||
msgid "%s selected processes have been marked as executed."
|
||||
msgstr ""
|
||||
|
||||
#: actions.py:150 models.py:165
|
||||
#: actions.py:150 models.py:173
|
||||
msgid "Aborted"
|
||||
msgstr ""
|
||||
|
||||
|
@ -133,15 +133,19 @@ msgstr ""
|
|||
msgid "Commit"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:43
|
||||
#: admin.py:44
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:105
|
||||
#: admin.py:106
|
||||
msgid "proc"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:158
|
||||
#: admin.py:129 templates/admin/payments/transaction/report.html:62
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:168
|
||||
msgid "Transactions"
|
||||
msgstr ""
|
||||
|
||||
|
@ -186,6 +190,18 @@ msgstr ""
|
|||
msgid "SEPA Direct Debit"
|
||||
msgstr ""
|
||||
|
||||
#: methods/sepadirectdebit.py:47
|
||||
msgid ""
|
||||
"The transaction is created and requires the generation of the SEPA direct "
|
||||
"debit XML file."
|
||||
msgstr ""
|
||||
|
||||
#: methods/sepadirectdebit.py:49
|
||||
msgid ""
|
||||
"SEPA Direct Debit XML file is generated but needs to be sent to the "
|
||||
"financial institution."
|
||||
msgstr ""
|
||||
|
||||
#: models.py:20
|
||||
msgid "account"
|
||||
msgstr ""
|
||||
|
@ -194,7 +210,7 @@ msgstr ""
|
|||
msgid "method"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:24 models.py:169
|
||||
#: models.py:24 models.py:177
|
||||
msgid "data"
|
||||
msgstr ""
|
||||
|
||||
|
@ -211,68 +227,90 @@ msgid "Waitting execution"
|
|||
msgstr ""
|
||||
|
||||
#: models.py:102
|
||||
msgid "bill"
|
||||
msgid ""
|
||||
"The transaction is created and requires processing by the specific payment "
|
||||
"method."
|
||||
msgstr ""
|
||||
|
||||
#: models.py:105
|
||||
msgid "source"
|
||||
#: models.py:104
|
||||
msgid ""
|
||||
"The transaction is processed and its pending execution on the related "
|
||||
"financial institution."
|
||||
msgstr ""
|
||||
|
||||
#: models.py:106
|
||||
msgid "The transaction is executed on the financial institution."
|
||||
msgstr ""
|
||||
|
||||
#: models.py:107
|
||||
msgid "The transaction ammount is secured."
|
||||
msgstr ""
|
||||
|
||||
#: models.py:108
|
||||
msgid ""
|
||||
"The transaction has failed and the ammount is lost, a new transaction should "
|
||||
"be created for recharging."
|
||||
msgstr ""
|
||||
|
||||
#: models.py:112
|
||||
msgid "bill"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:115
|
||||
msgid "source"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:117
|
||||
msgid "process"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:108 models.py:171
|
||||
#: models.py:118 models.py:179
|
||||
msgid "state"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:110
|
||||
#: models.py:120
|
||||
msgid "amount"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:112 models.py:172
|
||||
#: models.py:122 models.py:180
|
||||
msgid "created"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:113
|
||||
#: models.py:123
|
||||
msgid "modified"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:128
|
||||
#: models.py:138
|
||||
msgid "New transactions can not be allocated for this bill."
|
||||
msgstr ""
|
||||
|
||||
#: models.py:163 templates/admin/payments/transaction/report.html:63
|
||||
#: models.py:171 templates/admin/payments/transaction/report.html:63
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:166
|
||||
#: models.py:174
|
||||
msgid "Commited"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:170
|
||||
#: models.py:178
|
||||
msgid "file"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:173
|
||||
#: models.py:181
|
||||
msgid "updated"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:176
|
||||
#: models.py:184
|
||||
msgid "Transaction processes"
|
||||
msgstr ""
|
||||
|
||||
#: settings.py:12
|
||||
#, fuzzy, python-format
|
||||
#| msgid ""
|
||||
#| "This bill will be automatically charged to your bank account with IBAN "
|
||||
#| "number<br><strong>%s</strong>."
|
||||
#: settings.py:14
|
||||
msgid ""
|
||||
"<strong>Direct debit</strong>, this bill will be automatically charged to "
|
||||
"your bank account with IBAN number<br><strong>%(number)s</strong>."
|
||||
msgstr ""
|
||||
"<strong>Càrrec per domiciliació</strong>, aquesta factura es cobrarà "
|
||||
"automaticament en el teu compte bancari amb IBAN <br><strong>%s</strong>."
|
||||
"automaticament en el teu compte bancari amb IBAN <br><strong>%(number)s</strong>."
|
||||
|
||||
#: templates/admin/payments/transaction/report.html:38
|
||||
msgid "Summary"
|
||||
|
@ -299,10 +337,6 @@ msgstr ""
|
|||
msgid "Contact"
|
||||
msgstr ""
|
||||
|
||||
#: templates/admin/payments/transaction/report.html:62
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
|
||||
#: templates/admin/payments/transaction/report.html:64
|
||||
msgid "Updated"
|
||||
msgstr ""
|
||||
|
|
Binary file not shown.
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-07-15 12:08+0000\n"
|
||||
"POT-Creation-Date: 2015-10-08 12:14+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -52,7 +52,7 @@ msgstr ""
|
|||
msgid "Process"
|
||||
msgstr ""
|
||||
|
||||
#: actions.py:63 actions.py:134 models.py:97 models.py:164
|
||||
#: actions.py:63 actions.py:134 models.py:97 models.py:172
|
||||
msgid "Executed"
|
||||
msgstr ""
|
||||
|
||||
|
@ -112,7 +112,7 @@ msgstr ""
|
|||
msgid "%s selected processes have been marked as executed."
|
||||
msgstr ""
|
||||
|
||||
#: actions.py:150 models.py:165
|
||||
#: actions.py:150 models.py:173
|
||||
msgid "Aborted"
|
||||
msgstr ""
|
||||
|
||||
|
@ -133,15 +133,19 @@ msgstr ""
|
|||
msgid "Commit"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:43
|
||||
#: admin.py:44
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:105
|
||||
#: admin.py:106
|
||||
msgid "proc"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:158
|
||||
#: admin.py:129 templates/admin/payments/transaction/report.html:62
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
|
||||
#: admin.py:168
|
||||
msgid "Transactions"
|
||||
msgstr ""
|
||||
|
||||
|
@ -186,6 +190,18 @@ msgstr ""
|
|||
msgid "SEPA Direct Debit"
|
||||
msgstr ""
|
||||
|
||||
#: methods/sepadirectdebit.py:47
|
||||
msgid ""
|
||||
"The transaction is created and requires the generation of the SEPA direct "
|
||||
"debit XML file."
|
||||
msgstr ""
|
||||
|
||||
#: methods/sepadirectdebit.py:49
|
||||
msgid ""
|
||||
"SEPA Direct Debit XML file is generated but needs to be sent to the "
|
||||
"financial institution."
|
||||
msgstr ""
|
||||
|
||||
#: models.py:20
|
||||
msgid "account"
|
||||
msgstr ""
|
||||
|
@ -194,7 +210,7 @@ msgstr ""
|
|||
msgid "method"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:24 models.py:169
|
||||
#: models.py:24 models.py:177
|
||||
msgid "data"
|
||||
msgstr ""
|
||||
|
||||
|
@ -211,68 +227,90 @@ msgid "Waitting execution"
|
|||
msgstr ""
|
||||
|
||||
#: models.py:102
|
||||
msgid "bill"
|
||||
msgid ""
|
||||
"The transaction is created and requires processing by the specific payment "
|
||||
"method."
|
||||
msgstr ""
|
||||
|
||||
#: models.py:105
|
||||
msgid "source"
|
||||
#: models.py:104
|
||||
msgid ""
|
||||
"The transaction is processed and its pending execution on the related "
|
||||
"financial institution."
|
||||
msgstr ""
|
||||
|
||||
#: models.py:106
|
||||
msgid "The transaction is executed on the financial institution."
|
||||
msgstr ""
|
||||
|
||||
#: models.py:107
|
||||
msgid "The transaction ammount is secured."
|
||||
msgstr ""
|
||||
|
||||
#: models.py:108
|
||||
msgid ""
|
||||
"The transaction has failed and the ammount is lost, a new transaction should "
|
||||
"be created for recharging."
|
||||
msgstr ""
|
||||
|
||||
#: models.py:112
|
||||
msgid "bill"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:115
|
||||
msgid "source"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:117
|
||||
msgid "process"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:108 models.py:171
|
||||
#: models.py:118 models.py:179
|
||||
msgid "state"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:110
|
||||
#: models.py:120
|
||||
msgid "amount"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:112 models.py:172
|
||||
#: models.py:122 models.py:180
|
||||
msgid "created"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:113
|
||||
#: models.py:123
|
||||
msgid "modified"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:128
|
||||
#: models.py:138
|
||||
msgid "New transactions can not be allocated for this bill."
|
||||
msgstr ""
|
||||
|
||||
#: models.py:163 templates/admin/payments/transaction/report.html:63
|
||||
#: models.py:171 templates/admin/payments/transaction/report.html:63
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:166
|
||||
#: models.py:174
|
||||
msgid "Commited"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:170
|
||||
#: models.py:178
|
||||
msgid "file"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:173
|
||||
#: models.py:181
|
||||
msgid "updated"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:176
|
||||
#: models.py:184
|
||||
msgid "Transaction processes"
|
||||
msgstr ""
|
||||
|
||||
#: settings.py:12
|
||||
#, fuzzy, python-format
|
||||
#| msgid ""
|
||||
#| "This bill will be automatically charged to your bank account with IBAN "
|
||||
#| "number<br><strong>%s</strong>."
|
||||
#: settings.py:14
|
||||
msgid ""
|
||||
"<strong>Direct debit</strong>, this bill will be automatically charged to "
|
||||
"your bank account with IBAN number<br><strong>%(number)s</strong>."
|
||||
msgstr ""
|
||||
"<strong>Adeudo por domiciliación</strong>, esta factura se cobrará "
|
||||
"automaticamente en tu cuenta bancaria con IBAN <br><strong>%s</strong>."
|
||||
"automaticamente en tu cuenta bancaria con IBAN <br><strong>%(number)s</strong>."
|
||||
|
||||
#: templates/admin/payments/transaction/report.html:38
|
||||
msgid "Summary"
|
||||
|
@ -299,10 +337,6 @@ msgstr ""
|
|||
msgid "Contact"
|
||||
msgstr ""
|
||||
|
||||
#: templates/admin/payments/transaction/report.html:62
|
||||
msgid "State"
|
||||
msgstr ""
|
||||
|
||||
#: templates/admin/payments/transaction/report.html:64
|
||||
msgid "Updated"
|
||||
msgstr ""
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import re
|
||||
import sys
|
||||
import textwrap
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
@ -46,6 +47,7 @@ class WordpressMuBackend(ServiceController):
|
|||
raise RuntimeError(errors[0] if errors else 'Unknown %i error' % response.status_code)
|
||||
|
||||
def get_id(self, session, saas):
|
||||
blog_id = saas.data.get('blog_id')
|
||||
search = self.get_main_url()
|
||||
search += '/wp-admin/network/sites.php?s=%s&action=blogs' % saas.name
|
||||
regex = re.compile(
|
||||
|
@ -55,23 +57,31 @@ class WordpressMuBackend(ServiceController):
|
|||
content = session.get(search).content.decode('utf8')
|
||||
# Get id
|
||||
ids = regex.search(content)
|
||||
if not ids:
|
||||
if not ids and not blog_id:
|
||||
raise RuntimeError("Blog '%s' not found" % saas.name)
|
||||
ids = ids.groups()
|
||||
if len(ids) > 1:
|
||||
raise ValueError("Multiple matches")
|
||||
if ids:
|
||||
ids = ids.groups()
|
||||
if len(ids) > 1 and not blog_id:
|
||||
raise ValueError("Multiple matches")
|
||||
# Get wpnonce
|
||||
wpnonce = re.search(r'<span class="delete">(.*)</span>', content).groups()[0]
|
||||
try:
|
||||
wpnonce = re.search(r'<span class="delete">(.*)</span>', content).groups()[0]
|
||||
except TypeError:
|
||||
# No search results, try some luck
|
||||
wpnonce = content
|
||||
wpnonce = re.search(r'_wpnonce=([^"]*)"', wpnonce).groups()[0]
|
||||
return int(ids[0]), wpnonce
|
||||
return blog_id or int(ids[0]), wpnonce
|
||||
|
||||
def create_blog(self, saas, server):
|
||||
if saas.data.get('blog_id'):
|
||||
return
|
||||
|
||||
session = requests.Session()
|
||||
self.login(session)
|
||||
|
||||
# Check if blog already exists
|
||||
try:
|
||||
self.get_id(session, saas)
|
||||
blog_id, wpnonce = self.get_id(session, saas)
|
||||
except RuntimeError:
|
||||
url = self.get_main_url()
|
||||
url += '/wp-admin/network/site-new.php'
|
||||
|
@ -91,6 +101,16 @@ class WordpressMuBackend(ServiceController):
|
|||
# Validate response
|
||||
response = session.post(url, data=data)
|
||||
self.validate_response(response)
|
||||
blog_id = re.compile(r'<link id="wp-admin-canonical" rel="canonical" href="http(?:[^ ]+)/wp-admin/network/site-new.php\?id=([0-9]+)" />')
|
||||
content = response.content.decode('utf8')
|
||||
blog_id = blog_id.search(content).groups()[0]
|
||||
sys.stdout.write("Created blog ID: %s\n" % blog_id)
|
||||
saas.data['blog_id'] = int(blog_id)
|
||||
saas.save(update_fields=('data',))
|
||||
else:
|
||||
sys.stdout.write("Retrieved blog ID: %s\n" % blog_id)
|
||||
saas.data['blog_id'] = int(blog_id)
|
||||
saas.save(update_fields=('data',))
|
||||
|
||||
def delete_blog(self, saas, server):
|
||||
session = requests.Session()
|
||||
|
@ -122,32 +142,37 @@ class WordpressMuBackend(ServiceController):
|
|||
def save(self, saas):
|
||||
self.append(self.create_blog, saas)
|
||||
context = self.get_context(saas)
|
||||
context['IDENT'] = "b.domain = '%(domain)s'" % context
|
||||
if context['blog_id']:
|
||||
context['IDENT'] = "b.blog_id = '%(blog_id)s'" % context
|
||||
self.append(textwrap.dedent("""
|
||||
# Update custom URL mapping
|
||||
existing=( $(mysql -Nrs %(db_name)s --execute='
|
||||
existing=( $(mysql -Nrs %(db_name)s --execute="
|
||||
SELECT b.blog_id, b.domain, m.domain, b.path
|
||||
FROM wp_domain_mapping AS m, wp_blogs AS b
|
||||
WHERE m.blog_id = b.blog_id AND m.active AND b.domain = "%(domain)s";') )
|
||||
WHERE m.blog_id = b.blog_id AND m.active AND %(IDENT)s;") )
|
||||
if [[ ${existing[0]} != '' ]]; then
|
||||
# Clear custom domain
|
||||
if [[ "%(custom_domain)s" == "" ]]; then
|
||||
mysql %(db_name)s --execute="
|
||||
DELETE wp_domain_mapping AS m, wp_blogs AS b
|
||||
WHERE m.blog_id = b.blog_id AND m.active AND b.domain = '%(domain)s';
|
||||
DELETE FROM m
|
||||
USING wp_domain_mapping AS m, wp_blogs AS b
|
||||
WHERE m.blog_id = b.blog_id AND m.active AND %(IDENT)s';
|
||||
UPDATE wp_blogs
|
||||
SET path='/'
|
||||
WHERE blog_id=${existing[0]};"
|
||||
elif [[ "${existing[2]}" != "%(custom_domain)s" || "${existing[3]}" != "%(custom_path)s" ]]; then
|
||||
mysql %(db_name)s --execute='
|
||||
mysql %(db_name)s --execute="
|
||||
UPDATE wp_domain_mapping as m, wp_blogs as b
|
||||
SET m.domain = "%(custom_domain)s", b.path = "%(custom_path)s"
|
||||
WHERE m.blog_id = b.blog_id AND m.active AND b.domain = "%(domain)s";'
|
||||
SET m.domain = '%(custom_domain)s', b.path = '%(custom_path)s'
|
||||
WHERE m.blog_id = b.blog_id AND m.active AND %(IDENT)s';"
|
||||
fi
|
||||
else
|
||||
blog=( $(mysql -Nrs %(db_name)s --execute='
|
||||
SELECT blog_id, path FROM wp_blogs WHERE domain = "%(domain)s";') )
|
||||
mysql %(db_name)s --execute='
|
||||
elif [[ "%(custom_domain)s" != "" ]]; then
|
||||
blog=( $(mysql -Nrs %(db_name)s --execute="
|
||||
SELECT blog_id, path FROM wp_blogs WHERE domain = '%(domain)s';") )
|
||||
mysql %(db_name)s --execute="
|
||||
INSERT INTO wp_domain_mapping
|
||||
VALUES (blog_id, domain, active) ($blog_id, "%(custom_domain)s", 1);'
|
||||
(blog_id, domain, active) VALUES (${blog[0]}, '%(custom_domain)s', 1);"
|
||||
if [[ "${blog[1]}" != "%(custom_path)s" ]]; then
|
||||
mysql %(db_name)s --execute="
|
||||
UPDATE wp_blogs
|
||||
|
@ -165,6 +190,9 @@ class WordpressMuBackend(ServiceController):
|
|||
context = {
|
||||
'db_name': settings.SAAS_WORDPRESS_DB_NAME,
|
||||
'domain': domain,
|
||||
'custom_domain': '',
|
||||
'custom_path': '/',
|
||||
'blog_id': saas.data.get('blog_id', ''),
|
||||
}
|
||||
if saas.custom_url:
|
||||
custom_url = urlparse(saas.custom_url)
|
||||
|
|
|
@ -113,12 +113,14 @@ class SoftwareService(plugins.Plugin):
|
|||
return helpers.create_or_update_directive(self)
|
||||
|
||||
def delete_directive(self):
|
||||
directive = None
|
||||
try:
|
||||
old = type(self.instance).objects.get(pk=self.instance.pk)
|
||||
directive = self.get_directive(old)
|
||||
if old.custom_url:
|
||||
directive = self.get_directive(old)
|
||||
except ObjectDoesNotExist:
|
||||
pass
|
||||
else:
|
||||
return
|
||||
if directive is not None:
|
||||
directive.delete()
|
||||
|
||||
def save(self):
|
||||
|
|
|
@ -3,6 +3,8 @@ from django.utils.safestring import mark_safe
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
|
||||
from orchestra.forms import widgets
|
||||
|
||||
from .options import SoftwareService
|
||||
from .. import settings
|
||||
from ..forms import SaaSBaseForm
|
||||
|
@ -12,6 +14,8 @@ class WordPressForm(SaaSBaseForm):
|
|||
email = forms.EmailField(label=_("Email"), widget=forms.TextInput(attrs={'size':'40'}),
|
||||
help_text=_("A new user will be created if the above email address is not in the database.<br>"
|
||||
"The username and password will be mailed to this email address."))
|
||||
blog_id = forms.IntegerField(label=("Blog ID"), widget=widgets.SpanWidget, required=False,
|
||||
help_text=_("ID of this user on the GitLab server, the only attribute that not changes."))
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(WordPressForm, self).__init__(*args, **kwargs)
|
||||
|
@ -23,6 +27,7 @@ class WordPressForm(SaaSBaseForm):
|
|||
|
||||
class WordPressDataSerializer(serializers.Serializer):
|
||||
email = serializers.EmailField(label=_("Email"))
|
||||
blog_id = serializers.IntegerField(label=_("Blog ID"), required=False)
|
||||
|
||||
|
||||
class WordPressService(SoftwareService):
|
||||
|
@ -31,6 +36,6 @@ class WordPressService(SoftwareService):
|
|||
form = WordPressForm
|
||||
serializer = WordPressDataSerializer
|
||||
icon = 'orchestra/icons/apps/WordPress.png'
|
||||
change_readonly_fileds = ('email',)
|
||||
change_readonly_fileds = ('email', 'blog_id')
|
||||
site_domain = settings.SAAS_WORDPRESS_DOMAIN
|
||||
allow_custom_url = settings.SAAS_WORDPRESS_ALLOW_CUSTOM_URL
|
||||
|
|
|
@ -53,7 +53,9 @@
|
|||
<input type="text" id="searchbox" style="margin-left:15px;margin-top:7px;" name="q"
|
||||
placeholder="Search" size="25" value="{{ query }}"
|
||||
{% if search_autofocus or app_list %}autofocus="autofocus"{% endif %}
|
||||
title="Use 'username!' for account direct access.">
|
||||
title="Use 'accountname!' for account direct access
|
||||
Use 'service:word' for searching on specific services
|
||||
Use 'fieldname=word' for searching on specific fields">
|
||||
</form>
|
||||
<span style="float:right;color:grey;margin:10px;font-size:11px;">
|
||||
{% url 'admin:accounts_account_change' user.pk as user_change_url %}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<div>
|
||||
<div style="margin:20px;-webkit-column-count:{{ columns }};-moz-column-count:{{ columns }};column-count:{{ columns }};">
|
||||
{% for opts, qs in results.items %}
|
||||
<h3><a href="{% url opts|admin_urlname:'changelist' %}?q={{ query }}">{{ opts.verbose_name_plural|capfirst }}</a>
|
||||
<h3><a href="{% url opts|admin_urlname:'changelist' %}?q={{ search_term }}">{{ opts.verbose_name_plural|capfirst }}</a>
|
||||
<span style="font-size:11px"> {{ qs|length }} results</span></h3>
|
||||
<ul>
|
||||
{% for instance in qs %}
|
||||
|
|
Loading…
Reference in a new issue