From b6e4972e7f6aa08e0392a2cf441b60ab0e7d88b7 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Wed, 9 Dec 2009 09:09:53 -0700 Subject: Take 2: Extensible return values and validation; steps toward a single output_for_cli(); enable more webUI stuff --- ipawebui/__init__.py | 2 +- ipawebui/engine.py | 56 +++++++++++----- ipawebui/widgets.py | 186 ++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 222 insertions(+), 22 deletions(-) (limited to 'ipawebui') diff --git a/ipawebui/__init__.py b/ipawebui/__init__.py index 9f86da34..c7ebaa87 100644 --- a/ipawebui/__init__.py +++ b/ipawebui/__init__.py @@ -1,6 +1,6 @@ # Authors: Jason Gerard DeRose # -# Copyright (C) 2008 Red Hat +# Copyright (C) 2009 Red Hat # see file 'COPYING' for use and warranty information # # This program is free software; you can redistribute it and/or diff --git a/ipawebui/engine.py b/ipawebui/engine.py index ea0905d4..a90a450d 100644 --- a/ipawebui/engine.py +++ b/ipawebui/engine.py @@ -1,6 +1,6 @@ # Authors: Jason Gerard DeRose # -# Copyright (C) 2008 Red Hat +# Copyright (C) 2009 Red Hat # see file 'COPYING' for use and warranty information # # This program is free software; you can redistribute it and/or @@ -21,6 +21,7 @@ Engine to map ipalib plugins to wehjit widgets. """ from controllers import Command +from ipalib import crud class ParamMapper(object): def __init__(self, api, app): @@ -28,7 +29,7 @@ class ParamMapper(object): self._app = app self.__methods = dict() for name in dir(self): - if name.startswith('_') or name.endswith('_'): + if name.startswith('_'): continue attr = getattr(self, name) if not callable(attr): @@ -40,13 +41,12 @@ class ParamMapper(object): if key in self.__methods: method = self.__methods[key] else: - #raise Warning('No ParamMapper for %r' % key) method = self.Str return method(param, cmd) def Str(self, param, cmd): return self._app.new('TextRow', - label=param.cli_name, + label=param.label, name=param.name, required=param.required, value=param.default, @@ -61,7 +61,7 @@ class ParamMapper(object): def Flag(self, param, cmd): return self._app.new('SelectRow', name=param.name, - label=param.cli_name, + label=param.label, ) @@ -128,23 +128,43 @@ class Engine(object): return page def build_page(self, cmd): - page = self.app.new('PageApp', + page = self.app.new('PageCmd', + cmd=cmd, id=cmd.name, title=cmd.summary.rstrip('.'), ) - #page.form.action = self.app.url + '__json__' - page.actions.add( - self.app.new('Submit') + page.form.action = page.url + page.form.method = 'GET' + page.form.add( + self.app.new('Hidden', name='__mode__', value='output') ) - table = self.app.new('FieldTable') - page.view.add(table) - for param in cmd.params(): - if param.exclude and 'webui' in param.exclude: - continue - field = self.param_mapper(param, cmd) - table.add(field) + page.notification = self.app.new('Notification') + page.view.add(page.notification) + page.prompt = self.make_prompt(cmd) + page.show = self.make_show(cmd) + self.conditional('input', page.actions, self.app.new('Submit')) + self.conditional('input', page.view, page.prompt) + self.conditional('output', page.view, page.show) + return page + + def conditional(self, mode, parent, *children): + conditional = self.app.new('Conditional', mode=mode) + conditional.add(*children) + parent.add(conditional) - page.form.action = '/'.join([self.jsonurl, cmd.name]) + def make_prompt(self, cmd): + table = self.app.new('FieldTable') + for param in self._iter_params(cmd.params): + table.add( + self.param_mapper(param, cmd) + ) + return table + def make_show(self, cmd): + return self.app.new('Output') - return page + def _iter_params(self, namespace): + for param in namespace(): + if param.exclude and 'webui' in param.exclude: + continue + yield param diff --git a/ipawebui/widgets.py b/ipawebui/widgets.py index 74b9d7e0..1cf1adb9 100644 --- a/ipawebui/widgets.py +++ b/ipawebui/widgets.py @@ -1,6 +1,6 @@ # Authors: Jason Gerard DeRose # -# Copyright (C) 2008 Red Hat +# Copyright (C) 2009 Red Hat # see file 'COPYING' for use and warranty information # # This program is free software; you can redistribute it and/or @@ -21,9 +21,10 @@ Custom IPA widgets. """ from textwrap import dedent -from wehjit import Collection, base, freeze +from wehjit import Collection, base, freeze, builtins from wehjit.util import Alternator from wehjit import Static, Dynamic, StaticProp, DynamicProp +from ipaserver.rpcserver import extract_query class IPAPlugins(base.Container): @@ -189,6 +190,14 @@ class Command(base.Widget): + + + + + + + + """ @@ -211,7 +220,7 @@ class Object(base.Widget): - + ${"param.name"}: @@ -219,6 +228,171 @@ class Object(base.Widget): """ + +class Conditional(base.Container): + + mode = Static('mode', default='input') + + @DynamicProp + def page_mode(self): + if self.page is None: + return + return self.page.mode + + xml = """ +
+ +
+ """ + + +class Output(base.Widget): + """ + Shows attributes form an LDAP entry. + """ + + order = Dynamic('order') + labels = Dynamic('labels') + result = Dynamic('result') + + xml = """ +
+ + + +
+ +
+ + + + + + +
+
+
+
+ """ + + style = ( + ('table', ( + ('empty-cells', 'show'), + ('border-collapse', 'collapse'), + )), + + ('th', ( + ('text-align', 'right'), + ('padding', '.25em 0.5em'), + ('line-height', '%(height_bar)s'), + ('vertical-align', 'top'), + )), + + ('td', ( + ('padding', '.25em'), + ('vertical-align', 'top'), + ('text-align', 'left'), + ('line-height', '%(height_bar)s'), + )), + ) + + +class Hidden(base.Field): + xml = """ + + """ + + +class Notification(base.Widget): + message = Dynamic('message') + error = Dynamic('error', default=False) + + @property + def extra_css_classes(self): + if self.error: + yield 'error' + else: + yield 'okay' + + xml = """ +

+ """ + + style = ( + ('', ( + ('font-weight', 'bold'), + ('-moz-border-radius', '100%%'), + ('background-color', '#eee'), + ('border', '2px solid #966'), + ('padding', '0.5em'), + ('text-align', 'center'), + )), + ) + + +class PageCmd(builtins.PageApp): + cmd = Static('cmd') + mode = Dynamic('mode', default='input') + + def controller(self, environ): + query = extract_query(environ) + self.mode = query.pop('__mode__', 'input') + if self.mode == 'input': + return + soft = self.cmd.soft_validate(query) + errors = soft['errors'] + values = soft['values'] + if errors: + self.mode = 'input' + for key in self.form: + if key in errors: + self.form[key].error = errors[key] + if key in values: + self.form[key].value = values[key] + return + output = self.cmd(**query) + if isinstance(output, dict) and 'summary' in output: + self.notification.message = output['summary'] + params = self.cmd.output_params + if params: + order = list(params) + labels = dict((p.name, p.label) for p in params()) + else: + order = sorted(entry) + labels = dict((k, k) for k in order) + self.show.order = order + self.show.labels = labels + self.show.result = output.get('result') + + def create_widgets(): widgets = Collection('freeIPA') widgets.register_builtins() @@ -227,6 +401,12 @@ def create_widgets(): widgets.register(IPAPlugins) widgets.register(Command) widgets.register(Object) + widgets.register(Conditional) + widgets.register(Output) + widgets.register(Hidden) + widgets.register(Notification) + + widgets.register(PageCmd) freeze(widgets) -- cgit