From 38a46b5983d5e65d602a256a5f39000579f13d2d Mon Sep 17 00:00:00 2001 From: Marc Aymerich Date: Thu, 8 Oct 2015 13:54:39 +0000 Subject: [PATCH] Random fixes --- TODO.md | 3 +- orchestra/admin/__init__.py | 51 ++++++++-- orchestra/admin/options.py | 19 ++-- .../bills/locale/es/LC_MESSAGES/django.mo | Bin 4944 -> 4980 bytes .../bills/locale/es/LC_MESSAGES/django.po | 2 +- orchestra/contrib/bills/models.py | 2 +- orchestra/contrib/mailboxes/admin.py | 44 +++++++- orchestra/contrib/mailboxes/forms.py | 35 ------- orchestra/contrib/orchestration/methods.py | 23 +++-- .../payments/locale/ca/LC_MESSAGES/django.mo | Bin 421 -> 740 bytes .../payments/locale/ca/LC_MESSAGES/django.po | 94 ++++++++++++------ .../payments/locale/es/LC_MESSAGES/django.mo | Bin 421 -> 735 bytes .../payments/locale/es/LC_MESSAGES/django.po | 94 ++++++++++++------ .../contrib/saas/backends/wordpressmu.py | 66 ++++++++---- orchestra/contrib/saas/services/options.py | 8 +- orchestra/contrib/saas/services/wordpress.py | 7 +- orchestra/templates/admin/orchestra/menu.html | 4 +- .../templates/admin/orchestra/search.html | 2 +- 18 files changed, 305 insertions(+), 149 deletions(-) diff --git a/TODO.md b/TODO.md index 8303ec77..ddc5cd57 100644 --- a/TODO.md +++ b/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 diff --git a/orchestra/admin/__init__.py b/orchestra/admin/__init__.py index 49d603ab..d59c7fab 100644 --- a/orchestra/admin/__init__.py +++ b/orchestra/admin/__init__.py @@ -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, } diff --git a/orchestra/admin/options.py b/orchestra/admin/options.py index 920db520..c9a63fcd 100644 --- a/orchestra/admin/options.py +++ b/orchestra/admin/options.py @@ -80,17 +80,24 @@ class EnhaceSearchMixin(object): def get_search_results(self, request, queryset, search_term): """ allows to specify field : """ 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) diff --git a/orchestra/contrib/bills/locale/es/LC_MESSAGES/django.mo b/orchestra/contrib/bills/locale/es/LC_MESSAGES/django.mo index 8f1b04a91a6a3f1382e0fb404114a3ffaf0f00e2..6340463ce5c04b09a1dc25e9e5f324c9bc58146b 100644 GIT binary patch delta 1461 zcmXxkUrfzm9LMo*sg4tMDu;&DP)CwdAtisxIZHD+`a^V+noZ0scV_(jyKobm#<^h< zTQ*%tvsuSxbHUu0xoT!yg)tWzHg0%-&Ut#)_q?9p^E|)jd4AvD)9+2_XUO}Imv-GK zO+*1vo@#ag8`Aip45yoY!#ns42Q$p_aJSEFBle)~jXB?8nCoS8&9-1KZpC4w%RXWj ze#Trg&wf#vcf=s%75=%MC|q3*kh3-BHe z;1kSeeJjgMF4Tk?xYHR$##k2?VK0{A1q|T>)CS+87M{W!e2-fAJCZD$K|RnP)PtnX zOZErQ&-xZr1q)FFOHmuCLhi9TFJ!TqRl-LAb4weV3?ich%qGpKnlx&A@a zyw}jv4Y#Q1Nk&iu@1qtNckPc+H^lK+irEXi%5^oXJjLgz)Oy)w6*z>=YR5Xv3?vO> z4cBL#j{@XhH-4f){>6ouO&)h(F=~THkTvZ%Dg&obPk0Ws;VZ5`=C1F${xM_^Hi=8| z6>7d|ROY_skbf;aL&JH@;7BgU%cy}P&Qa$W>Q#+nImS^>^u<}gzlJgyMP+awYJ&$| zdmn1ye&iM#@Th157hOjT*^AvqrTii4$>XRee~H?_Yt&(ya{be&4gWwb_#3s*Kc|mH z)E_{#2T|j^0xFug1eMZqH=y3NH=zc$INMMg=tiaXFmfakrHo&FL-buxSxIP4mF+~e zDwGmpEx~`4XKSeFL%~K9rH0>agwi)#N~r`19VU)QqUdQh5vvLIkSMiIp-paZ*L>3w zrP3)Hi3VGhOs`V1oG2z#l#xP0??|tsVkj%U=n#VwMfuP%-Aw3hR}m|S*%G3%j?f8F zj`aPjCl(P)2o;?&eK%EfH1)FAPYkBV)BJloBkdh6ds^Eknlqz5f3z*q+}YaE*gkP1 HcQWNaz;tt~ delta 1431 zcmXxkOGwmF6vy#1HKtiUD@}VyrK8!TrTHi`ogQS+Ly8uzBzq7EM%bbV8KQ*-Rz3=Y zh>A86feTv%MNzcsK~h9QP(cKNEnFB$tG>VgoVj@Cb6@j6_nx`_XCf1k_}AR<4Wq=U z%c;e4%-S#-=7TbjVm5_?_#AuYndM?*s#z_z;&$wHU*k^t8ELG+gSZU`kSTkQ8Tc7< z%;NTwMkND(Q87#Bo6)o=64PpMAvT~M(Bkv0ZkyYIbD8hNd_0YMUN7o<{kRCP;~5;q zeBxVSdLmF2YGJ+Gj79WYunbRP1P4(YdyESB472eiD&R*XNj8Z($T!phe);?#B)0vN z#JBK*#KKI}2J(?PD?!b<3~{&IOX%*sOO&Z{w0j- zBz-ir@G2_62x|Nm>I?UF z-rFqluf}%MLIvD~EVX^8 z!0kTX9j8HTyMRh{KkCFIs1x5uU6u!^OZ3R+U!XSn4i)eND$p1AhtE%We;T!J7WLc^ zyHFY>T9(Hq66?CHO$oVu0D zK2+jX#iw2nN?)>+(pXHbr*b8NqSMw<%c$%jD0%@Ui#EB*JG{e!QsIhbY6Df-)q$I+ zYl1fgjo{>{-$*~l`r#ERMMomERFn^0&n;B_svD@QsL4`7qmsIgsvPOPr~98rEu^aG jcEy5L;!5hrt{%Ug@+3Uok$xaGw0F2Gr(pb3&YjSIaBXb> diff --git a/orchestra/contrib/bills/locale/es/LC_MESSAGES/django.po b/orchestra/contrib/bills/locale/es/LC_MESSAGES/django.po index 1a4f89a1..460c7c61 100644 --- a/orchestra/contrib/bills/locale/es/LC_MESSAGES/django.po +++ b/orchestra/contrib/bills/locale/es/LC_MESSAGES/django.po @@ -538,7 +538,7 @@ msgstr "" #: templates/bills/microspective.html:149 msgid "QUESTIONS" -msgstr "" +msgstr "PREGUNTAS" #: templates/bills/microspective.html:150 #, python-format diff --git a/orchestra/contrib/bills/models.py b/orchestra/contrib/bills/models.py index 783016f7..8a1715a4 100644 --- a/orchestra/contrib/bills/models.py +++ b/orchestra/contrib/bills/models.py @@ -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() diff --git a/orchestra/contrib/mailboxes/admin.py b/orchestra/contrib/mailboxes/admin.py index 5cc4e8a5..7f77d6e5 100644 --- a/orchestra/contrib/mailboxes/admin.py +++ b/orchestra/contrib/mailboxes/admin.py @@ -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') @@ -223,6 +242,29 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin): def get_queryset(self, request): 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) diff --git a/orchestra/contrib/mailboxes/forms.py b/orchestra/contrib/mailboxes/forms.py index a377c765..cbfbf71c 100644 --- a/orchestra/contrib/mailboxes/forms.py +++ b/orchestra/contrib/mailboxes/forms.py @@ -44,24 +44,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): @@ -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 diff --git a/orchestra/contrib/orchestration/methods.py b/orchestra/contrib/orchestration/methods.py index a37a2d9a..54a58916 100644 --- a/orchestra/contrib/orchestration/methods.py +++ b/orchestra/contrib/orchestration/methods.py @@ -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() diff --git a/orchestra/contrib/payments/locale/ca/LC_MESSAGES/django.mo b/orchestra/contrib/payments/locale/ca/LC_MESSAGES/django.mo index b11325075be298ef93cc3804f7961595977aa6dc..647bb80b7c1570671599da5585ea868946be17f4 100644 GIT binary patch delta 402 zcmZvYze>bF5QlT>Rf2`B7Sjj{Q7?#aNI39Ma~7gtEzEAl%W_Hf+{_|Z-3!=Qh}d1@ z1Bj21_qfUjabmn+;lQ`c4)bF^_I>rMJ6mk7y#!J#!nP0!T_G0gx)z><8)4iDf`!~~ z!leY{*;bwZskmfki_zf1IMNIWDZ}yMZ!-i=O$o|m89dgM+;m&T;+bi332haW6^{BVs>q8Ws93OqW zZgghL`SWV-L=h(BAhEg8CNro__1VTlzz0tyBiy5A?+~a2ZIwe;&HrfUBx+OuWx(WR YBA+nT;u@U+%vGb@$Zj>?^Pg?-4a{PA{{R30 delta 81 zcmaFDx|G@Co)F7a1|VPrVi_P-0b*t#)&XJ=umIw0prj>`2C0F8iFZS|4a{{7O%)7{ NtPCtR+c5Gm0swoI35);$ diff --git a/orchestra/contrib/payments/locale/ca/LC_MESSAGES/django.po b/orchestra/contrib/payments/locale/ca/LC_MESSAGES/django.po index 302a0436..07f2e6a0 100644 --- a/orchestra/contrib/payments/locale/ca/LC_MESSAGES/django.po +++ b/orchestra/contrib/payments/locale/ca/LC_MESSAGES/django.po @@ -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 \n" "Language-Team: LANGUAGE \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
%s." +#: settings.py:14 msgid "" "Direct debit, this bill will be automatically charged to " "your bank account with IBAN number
%(number)s." msgstr "" "Càrrec per domiciliació, aquesta factura es cobrarà " -"automaticament en el teu compte bancari amb IBAN
%s." +"automaticament en el teu compte bancari amb IBAN
%(number)s." #: 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 "" diff --git a/orchestra/contrib/payments/locale/es/LC_MESSAGES/django.mo b/orchestra/contrib/payments/locale/es/LC_MESSAGES/django.mo index b11325075be298ef93cc3804f7961595977aa6dc..de1e91c13224550e19053d6b759d66943c24b4a2 100644 GIT binary patch delta 397 zcmZvWu}T9$5QZ;mD#60S%3>Np;nbX11UU{OHl`2-YhiY8%`LgxmAP3YRbIeGP^>L{ z0Pzv>9`RM2Gv-L)58n(k%+LHE>)+k!e0$?H60HflLM(KJRA_W9JP9|#xD!Ql=`X@i z3^KNA{J%F`(8yI2I2VaCtts0v9S}C{>g#AMWihb_aF)?&!IHyP5P3dwobw&XUv5x2?`} zc|XWa$UQuGg4|bD+tQ-8voF`$V=y%%+@t0|2(s?N1Lz`2C0F8iFZS|4a{{7O%)7{ NtPCtR+c5Gm0swjc35Ngx diff --git a/orchestra/contrib/payments/locale/es/LC_MESSAGES/django.po b/orchestra/contrib/payments/locale/es/LC_MESSAGES/django.po index be4b79b7..58e0828c 100644 --- a/orchestra/contrib/payments/locale/es/LC_MESSAGES/django.po +++ b/orchestra/contrib/payments/locale/es/LC_MESSAGES/django.po @@ -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 \n" "Language-Team: LANGUAGE \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
%s." +#: settings.py:14 msgid "" "Direct debit, this bill will be automatically charged to " "your bank account with IBAN number
%(number)s." msgstr "" "Adeudo por domiciliación, esta factura se cobrará " -"automaticamente en tu cuenta bancaria con IBAN
%s." +"automaticamente en tu cuenta bancaria con IBAN
%(number)s." #: 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 "" diff --git a/orchestra/contrib/saas/backends/wordpressmu.py b/orchestra/contrib/saas/backends/wordpressmu.py index 9b64f5f4..565922a6 100644 --- a/orchestra/contrib/saas/backends/wordpressmu.py +++ b/orchestra/contrib/saas/backends/wordpressmu.py @@ -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'(.*)', content).groups()[0] + try: + wpnonce = re.search(r'(.*)', 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'') + 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) diff --git a/orchestra/contrib/saas/services/options.py b/orchestra/contrib/saas/services/options.py index f4fd5003..5e6254f8 100644 --- a/orchestra/contrib/saas/services/options.py +++ b/orchestra/contrib/saas/services/options.py @@ -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): diff --git a/orchestra/contrib/saas/services/wordpress.py b/orchestra/contrib/saas/services/wordpress.py index 3dd72853..0a14bff1 100644 --- a/orchestra/contrib/saas/services/wordpress.py +++ b/orchestra/contrib/saas/services/wordpress.py @@ -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.
" "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 diff --git a/orchestra/templates/admin/orchestra/menu.html b/orchestra/templates/admin/orchestra/menu.html index 9adee58f..40188e4b 100644 --- a/orchestra/templates/admin/orchestra/menu.html +++ b/orchestra/templates/admin/orchestra/menu.html @@ -53,7 +53,9 @@ + title="Use 'accountname!' for account direct access +Use 'service:word' for searching on specific services +Use 'fieldname=word' for searching on specific fields"> {% url 'admin:accounts_account_change' user.pk as user_change_url %} diff --git a/orchestra/templates/admin/orchestra/search.html b/orchestra/templates/admin/orchestra/search.html index 477e62e7..b5ea6e26 100644 --- a/orchestra/templates/admin/orchestra/search.html +++ b/orchestra/templates/admin/orchestra/search.html @@ -7,7 +7,7 @@
{% for opts, qs in results.items %} -

{{ opts.verbose_name_plural|capfirst }} +

{{ opts.verbose_name_plural|capfirst }} {{ qs|length }} results

    {% for instance in qs %}