diff --git a/TODO.md b/TODO.md index efb56e1d..ac85f71e 100644 --- a/TODO.md +++ b/TODO.md @@ -93,7 +93,7 @@ Php binaries should have this format: /usr/bin/php5.2-cgi * logs on panel/logs/ ? mkdir ~webapps, backend post save signal? * and other IfModule on backend SecRule -* Orchestra global search box on the page head, based https://github.com/django/django/blob/master/django/contrib/admin/options.py#L866 and iterating over all registered services and inspectin its admin.search_fields +# Orchestra global search box on the page head, based https://github.com/django/django/blob/master/django/contrib/admin/options.py#L866 and iterating over all registered services and inspectin its admin.search_fields * contain error on plugin missing key (plugin dissabled): NOP, fail hard is better than silently, perhaps fail at starttime? apploading machinary @@ -131,6 +131,7 @@ require_once(‘/etc/moodles/’.$moodle_host.‘config.php’);``` moodle/drupl * document service help things: discount/refound/compensation effect and metric table * Document metric interpretation help_text * document plugin serialization, data_serializer? +* Document strong input validation # bill line managemente, remove, undo (only when possible), move, copy, paste * budgets: no undo feature @@ -415,3 +416,11 @@ mkhomedir_helper or create ssh homes with bash.rc and such # setupforbiddendomains --url alexa -n 5000 + + +* remove welcome box on dashboard? + +# account contacts inline, show provided fields and ignore the rest? +# email usage -webkit-column-count:3;-moz-column-count:3;column-count:3; + +# resources on service report diff --git a/orchestra/admin/dashboard.py b/orchestra/admin/dashboard.py index 1e09822d..5cded62c 100644 --- a/orchestra/admin/dashboard.py +++ b/orchestra/admin/dashboard.py @@ -19,6 +19,15 @@ class AppDefaultIconList(CmsAppIconList): class OrchestraIndexDashboard(dashboard.FluentIndexDashboard): """ Gets application modules from services, accounts and administration registries """ + + def __init__(self, **kwargs): + super(dashboard.FluentIndexDashboard, self).__init__(**kwargs) + self.children.append(self.get_personal_module()) + self.children.extend(self.get_application_modules()) + recent_actions = self.get_recent_actions_module() + recent_actions.enabled = True + self.children.append(recent_actions) + def process_registered_view(self, module, view_name, options): app_name, name = view_name.split('_')[:-1] module.icons['.'.join((app_name, name))] = options.get('icon') @@ -44,7 +53,7 @@ class OrchestraIndexDashboard(dashboard.FluentIndexDashboard): # Honor settings override, hacky. I Know if appsettings.FLUENT_DASHBOARD_APP_GROUPS[0][0] != _('CMS'): modules = super(OrchestraIndexDashboard, self).get_application_modules() - for register in (accounts, administration, services): + for register in (accounts, services, administration): title = register.verbose_name models = [] icons = {} diff --git a/orchestra/contrib/accounts/actions.py b/orchestra/contrib/accounts/actions.py index f5703ebd..2dc16117 100644 --- a/orchestra/contrib/accounts/actions.py +++ b/orchestra/contrib/accounts/actions.py @@ -49,7 +49,7 @@ def service_report(modeladmin, request, queryset): model = field.related_model if model in registered_services and model != queryset.model: fields.append((model, name)) - sorted(fields, key=lambda f: f[0]._meta.verbose_name_plural.lower()) + fields = sorted(fields, key=lambda f: f[0]._meta.verbose_name_plural.lower()) fields = [field for model, field in fields] for account in queryset.prefetch_related(*fields): diff --git a/orchestra/contrib/accounts/templates/admin/accounts/account/service_report.html b/orchestra/contrib/accounts/templates/admin/accounts/account/service_report.html index 1ac7a5fb..f10d116f 100644 --- a/orchestra/contrib/accounts/templates/admin/accounts/account/service_report.html +++ b/orchestra/contrib/accounts/templates/admin/accounts/account/service_report.html @@ -1,4 +1,4 @@ -{% load utils i18n %} +{% load i18n admin_urls utils %} {% block title %}Account service report{% endblock %} @@ -50,13 +50,29 @@
{{ account.get_type_display }} {% trans "account registered on" %} {{ account.date_joined | date }}
diff --git a/orchestra/templates/admin/orchestra/search.html b/orchestra/templates/admin/orchestra/search.html new file mode 100644 index 00000000..477e62e7 --- /dev/null +++ b/orchestra/templates/admin/orchestra/search.html @@ -0,0 +1,19 @@ +{% extends "admin/base_site.html" %} +{% load i18n l10n %} +{% load url from future %} +{% load admin_urls static utils %} + +{% block content %} +
+
+ {% for opts, qs in results.items %} +

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

+ + {% endfor %} +
+{% endblock %} diff --git a/orchestra/urls.py b/orchestra/urls.py index 72057de3..d2aca735 100644 --- a/orchestra/urls.py +++ b/orchestra/urls.py @@ -24,6 +24,7 @@ urlpatterns = [ 'orchestra.views.serve_private_media', name='private-media' ), + url(r'search', 'orchestra.views.search', name='search'), ] diff --git a/orchestra/views.py b/orchestra/views.py index 31067678..06424393 100644 --- a/orchestra/views.py +++ b/orchestra/views.py @@ -1,10 +1,22 @@ +import itertools +from collections import OrderedDict + from django.http import Http404 +from django.contrib import admin from django.contrib.admin.utils import unquote from django.core.exceptions import PermissionDenied +from django.core.urlresolvers import reverse from django.db.models import get_model -from django.shortcuts import get_object_or_404 +from django.shortcuts import get_object_or_404, render, redirect +from django.utils.safestring import mark_safe +from django.utils.translation import ugettext_lazy as _ from django.views.static import serve +from orchestra.contrib.accounts.models import Account + +from .core import accounts, services +from .utils.python import OrderedSet + def serve_private_media(request, app_label, model_name, field_name, object_id, filename): model = get_model(app_label, model_name) @@ -18,3 +30,48 @@ def serve_private_media(request, app_label, model_name, field_name, object_id, f return serve(request, field.name, document_root=field.storage.location) else: raise PermissionDenied() + + +def search(request): + query = request.GET.get('q', '') + if query.endswith('!'): + # Account direct access + query = query.replace('!', '') + try: + account = Account.objects.get(username=query) + except Account.DoesNotExist: + pass + else: + account_url = reverse('admin:accounts_account_change', args=(account.pk,)) + return redirect(account_url) + 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: + try: + modeladmin = admin.site._registry[model] + except KeyError: + pass + else: + qs = modeladmin.get_queryset(request) + qs, search_use_distinct = modeladmin.get_search_results(request, qs, query) + if search_use_distinct: + qs = qs.distinct() + num = len(qs) + if num: + total += num + results[model._meta] = qs + title = _("{total} search results for '{query}'").format(total=total, query=query) + context = { + 'title': mark_safe(title), + 'total': total, + 'columns': min(int(total/17), 3), + 'query': query, + 'results': results, + 'search_autofocus': True, + } + return render(request, 'admin/orchestra/search.html', context)