Exemplo n.º 1
0
Arquivo: test.py Projeto: cerha/pytis
 def test_prefill_untouched(self):
     row = self._row((
         pp.Field('a', type=pd.Integer()),
         pp.Field('b', type=pd.Integer()),
     ))
     prefill = dict(a=5, b=8)
     row.set_row(None, reset=True, prefill=prefill)
     assert prefill == dict(a=5, b=8)
Exemplo n.º 2
0
Arquivo: test.py Projeto: cerha/pytis
 def test_completer(self):
     completer = self._enumerator(
         ('x', ),
         data=(('Apple', ), ('Bananas', ), ('Basil', ), ('Bacardi', ),
               ('Cinamon', )),
     )
     fields = (
         pp.Field('a', type=pd.Integer(not_null=True)),
         pp.Field('x0',
                  type=pd.String(enumerator=pd.FixedEnumerator(('a', 'b',
                                                                'c')))),
         pp.Field('x1', type=pd.String(), completer=('yes', 'no', 'maybe')),
         pp.Field('x2',
                  type=pd.String(),
                  completer=completer,
                  runtime_filter=pp.computer(lambda r, a: pd.NE(
                      'x', pd.sval('Bacardi')) if a == 1 else None)),
     )
     row = self._row(fields, new=True)
     assert not row.has_completer('a')
     assert row.has_completer('x0')
     assert row.has_completer('x1', static=True)
     assert row.has_completer('x2')
     assert not row.has_completer('x2', static=True)
     assert row.completions('a') == []
     assert row.completions('x0') == ['a', 'b', 'c']
     assert row.completions('x1') == ['maybe', 'no', 'yes']
     assert row.completions('x1', prefix='y') == ['yes']
     assert row.completions('x2') == ('Apple', 'Bananas', 'Basil',
                                      'Bacardi', 'Cinamon')
     assert row.completions('x2',
                            prefix='ba') == ('Bananas', 'Basil', 'Bacardi')
     row['a'] = 1
     assert row.completions('x2', prefix='ba') == ('Bananas', 'Basil')
Exemplo n.º 3
0
 def test_display(self):
     C = pd.ColumnSpec
     S = pd.String
     V = pd.Value
     rows = [
         pd.Row((('x', V(S(), x)), ('y', V(S(), y))))
         for x, y in (('1', 'FIRST'), ('2', 'SECOND'), ('3', 'THIRD'))
     ]
     edata = pd.DataFactory(pd.MemData, (C('x', S()), C('y', S())),
                            data=rows)
     enum = pd.DataEnumerator(edata)
     key = C('a', pd.Integer())
     columns = (key, C('b', S(enumerator=enum)), C('c', S(enumerator=enum)),
                C('d', S(enumerator=enum)))
     data = pd.Data(columns, key)
     fields = (
         Field('a'),
         Field('b', display='y'),
         Field('c', display=lambda x: '-' + x + '-'),
         Field('d', display=lambda row: row['y'].value().lower()),
     )
     row = pp.PresentedRow(fields,
                           data,
                           None,
                           prefill={
                               'b': '2',
                               'c': '3',
                               'd': '1'
                           })
     self.assertEqual(row.display('a'), '')
     self.assertEqual(row.display('b'), 'SECOND')
     self.assertEqual(row.display('c'), '-3-')
     self.assertEqual(row.display('d'), 'first')
Exemplo n.º 4
0
Arquivo: test.py Projeto: cerha/pytis
 def test_original_row(self):
     r = pd.Row([(k, pd.Value(pd.Integer(), v))
                 for k, v in dict(a=None, b=6, c=None, d=3).items()])
     row = self._mega_row(new=True, row=r, b=22, c=33, d=44)
     r1 = row.original_row()
     self._check_values(r1, a=None, b=22, c=33, d=44)
     r2 = row.original_row(initialized=False)
     self._check_values(r2, a=None, b=6, c=None, d=3)
Exemplo n.º 5
0
 class Specification(pp.Specification):
     table = 'grid_test'
     fields = (
         pp.Field('id', type=pd.Integer()),
         pp.Field('name', type=pd.String()),
         pp.Field('price', type=pd.Float(precision=2)),
         pp.Field('flag', type=pd.Boolean()),
     )
Exemplo n.º 6
0
Arquivo: test.py Projeto: cerha/pytis
    def test_str(self):
        class BigString(pd.String, pd.Big):
            pass

        row = self._row((
            pp.Field('x', type=pd.Integer(not_null=True)),
            pp.Field('y', type=pd.Integer(), default=88),
            pp.Field('passwd', type=pd.Password()),
            pp.Field('data', type=BigString()),
        ),
                        new=True,
                        prefill=dict(x=1,
                                     y=3,
                                     passwd='secret',
                                     data=1024 * 'x'))
        assert unistr(
            row
        ) == '<PresentedRow: x=1, y=3, passwd=***, data=<BigString 1 kB>>'
Exemplo n.º 7
0
Arquivo: test.py Projeto: cerha/pytis
 class Fruits(pytis.presentation.Specification):
     fields = (
         pp.Field('id'),
         pp.Field('title'),
         pp.Field('code', type=pd.Integer()),
         pp.Field('tropical', type=pd.Boolean()),
     )
     data_cls = pd.MemData
     data = (('apl', 'Apple', 123, False), ('ban', 'Banana', 234, True),
             ('str', 'Strawberry', 234, False), ('org', 'Orange', 456,
                                                 True))
Exemplo n.º 8
0
Arquivo: test.py Projeto: cerha/pytis
    def test_recursive_computer_validation(self):
        fields = (
            pp.Field('a',
                     type=pd.Integer(not_null=True),
                     computer=pp.computer(lambda r, b: 2 * b, validate=True)),
            pp.Field(
                'b',
                type=pd.Integer(not_null=True,
                                enumerator=pd.FixedEnumerator(range(101))),
                runtime_filter=pp.computer(lambda r, a: lambda x: x % a == 0,
                                           validate=True)),
        )
        # The computer for 'a' is called to compute the initial value and will lead to recursion
        # because it requires validation of 'b' which needs the value of 'a'...
        with pytest.raises(RuntimeError):
            self._row(fields, new=True)

        class Specification(pp.Specification):
            pass

        Specification.fields = fields
        # ViewSpec initialization should detect the cyclic dependency.
        with pytest.raises(AssertionError):
            Specification().view_spec()

        class Specification2(Specification):
            def _customize_fields(self, fields):
                fields.modify('b',
                              runtime_filter=pp.computer(
                                  lambda r, a: lambda x: x % a == 0,
                                  validate=True,
                                  novalidate=('a', )))

        row = self._row(Specification2().view_spec().fields(), new=True)
        # 'a' is None so runtime_filter will try to compute x % None (because 'a' is not validated).
        with pytest.raises(TypeError):
            row.enumerate('b')
Exemplo n.º 9
0
    def setUp(self):
        self.longMessage = True
        key = pd.ColumnSpec('a', pd.Integer())
        self._columns = (key, pd.ColumnSpec('b', pd.Integer()),
                         pd.ColumnSpec('c', pd.Integer()),
                         pd.ColumnSpec('d', pd.Integer()),
                         pd.ColumnSpec('r', pd.IntegerRange()))
        self._data = pd.Data(self._columns, key)

        def twice(row):
            # Just try if it is possible to access the original row.
            row.original_row()['c'].value()
            c = row['c'].value()
            return c is not None and c * 2 or None

        def sum(row):
            b, c = (row['b'].value(), row['c'].value())
            if b is None or c is None:
                return 0
            return b + c

        def inc(row):
            sum = row['sum'].value()
            return sum is not None and sum + 1 or None

        def gt5(row):
            return row['sum'].value() > 5

        self._fields = (
            pp.Field('a'),
            pp.Field('b'),
            pp.Field('c', default=lambda: 5),
            pp.Field('d',
                     editable=pp.Computer(gt5, depends=('sum', )),
                     computer=pp.Computer(twice, depends=('c', ))),
            pp.Field('e', type=pd.Integer(), virtual=True, default=88),
            pp.Field('sum',
                     type=pd.Integer(),
                     virtual=True,
                     editable=pp.Editable.NEVER,
                     computer=pp.Computer(sum, depends=('b', 'c'))),
            pp.Field('inc',
                     type=pd.Integer(),
                     virtual=True,
                     editable=pp.Editable.NEVER,
                     computer=pp.Computer(inc, depends=('sum', ))),
            pp.Field('r'),
        )
Exemplo n.º 10
0
Arquivo: test.py Projeto: cerha/pytis
 def test_editable_always_bool(self):
     row = self._row((
         pp.Field('a', type=pd.String()),
         pp.Field('b', type=pd.Integer()),
         pp.Field('c', editable=pp.computer(lambda r, a, b: a or b)),
     ))
     for a, b, editable in (
         (None, None, False),
         ('', None, False),
         (None, 0, False),
         ('', 0, False),
         (None, 1, True),
         ('', 5, True),
         ('x', None, True),
         ('y', 8, True),
     ):
         row['a'] = a
         row['b'] = b
         assert row.editable('c') is editable
Exemplo n.º 11
0
class GenericActions(Actions):
    title = _("Akce společné pro všechny položky men")
    help = _("Výčet podporovaných akcí společných pro všechny položky menu.")
    condition = pd.EQ('mod_id', pd.Value(pd.Integer(), None))
    access_rights = pd.AccessRights((None, ('cms_admin', pd.Permission.ALL)),
                                    (None, ('cms_user', pd.Permission.VIEW)))
Exemplo n.º 12
0
Arquivo: test.py Projeto: cerha/pytis
    def _mega_row(self, new=False, row=None, singleline=False, **prefill):
        # TODO: This all-in-one row was historically used for all tests
        # but it proved to be problematic.  You can not see the definition
        # of the fields from paticular tests so it is hard to guess the test
        # logic.  Morover the specification is getting too complicated
        # and hard to maintain usable in all situations.  Thus new tests should
        # Define their own fields directly just for their purpose.

        class BigString(pd.String, pd.Big):
            pass

        class SpecialEnumerator(pd.FixedEnumerator):
            # This class is overriden just to allow definition of runtime_filter
            # and runtime_arguments for the same field (which is only important
            # to improve test coverage)

            def values(self, a=None):
                # Accepts argument a as returned by runtime_arguments.
                return super(SpecialEnumerator, self).values()

        fields = (
            pp.Field('a', type=pd.Integer(not_null=True)),
            pp.Field('b',
                     type=pd.Integer(not_null=True,
                                     enumerator=SpecialEnumerator(range(101))),
                     runtime_filter=pp.computer(
                         lambda r, a: lambda x: x % a == 0, validate=True),
                     runtime_arguments=pp.computer(lambda r, a: dict(a=a))),
            pp.Field('c', type=pd.Integer(not_null=True), default=lambda: 5),
            pp.Field('d',
                     type=pd.Integer(),
                     editable=pp.computer(lambda r, total: total > 5
                                          if total else False),
                     computer=pp.computer(lambda r, c: c * 2, validate=True)),
            pp.Field('fruit',
                     type=pd.String(),
                     codebook='Fruits',
                     display='title',
                     null_display='none'),
            pp.Field('fruit_code',
                     virtual=True,
                     computer=pp.CbComputer('fruit', 'code')),
            pp.Field('range',
                     type=pd.IntegerRange(),
                     visible=pp.computer(lambda r, a: a != 0)),
            pp.Field('total',
                     type=pd.Integer(),
                     virtual=True,
                     editable=pp.Editable.NEVER,
                     computer=pp.computer(lambda r, b, c: b + c,
                                          validate=True,
                                          fallback=0)),
            pp.Field('half_total',
                     type=pd.Integer(),
                     virtual=True,
                     editable=pp.Editable.NEVER,
                     computer=pp.computer(lambda r, total: total // 2
                                          if total is not None else None)),
            pp.Field('x', type=pd.Integer(), virtual=True, default=88),
            pp.Field('password', type=pd.Password(), virtual=True),
            pp.Field('big', type=BigString(), virtual=True),
            pp.Field('array',
                     type=pd.Array,
                     inner_type=pd.String,
                     codebook='Fruits',
                     display='title',
                     virtual=True),
        )

        class Fruits(pytis.presentation.Specification):
            fields = (
                pp.Field('id'),
                pp.Field('title'),
                pp.Field('code', type=pd.Integer()),
                pp.Field('tropical', type=pd.Boolean()),
            )
            data_cls = pd.MemData
            data = (('apl', 'Apple', 123, False), ('ban', 'Banana', 234, True),
                    ('str', 'Strawberry', 234, False), ('org', 'Orange', 456,
                                                        True))

        class TestResolver(pytis.util.Resolver):
            _specifications = {'Fruits': Fruits}

            def _get_object_by_name(self, name):
                try:
                    return self._specifications[name]
                except KeyError:
                    return super(TestResolver, self)._get_object_by_name(name)

        pytis.config.resolver = TestResolver()
        return self._row(fields,
                         row=row,
                         new=new,
                         singleline=singleline,
                         prefill=prefill)
Exemplo n.º 13
0
Arquivo: test.py Projeto: cerha/pytis
 def test_key(self):
     row = self._row(
         (pp.Field('x', type=pd.Integer(not_null=True)), pp.Field('y')))
     assert row.key() == (pd.Value(pd.Integer(not_null=True), None), )
     row['x'] = 1
     assert row.key() == (pd.Value(pd.Integer(not_null=True), 1), )
Exemplo n.º 14
0
 def fields(self):
     return (
         Field('menu_id'),
         Field('menu_item_id'),
         Field('tree_order'),
         Field('tree_order_nsub'),
         Field(
             'identifier',
             _("Identifier"),
             width=20,
             fixed=True,
             editable=ONCE,
             type=pd.RegexString(maxlen=32,
                                 not_null=True,
                                 regex='^[a-zA-Z][0-9a-zA-Z_-]*$'),
             descr=
             _("Identifikátor bude vystupovat jako vnější adresa stránky.  "
               "Může být použit "
               "v odkazech na tuto stránku v rámci textu jiných stránek. Platný "
               "identifikátor může obsahovat pouze písmena bez diakritiky, číslice, "
               "pomlčky a podtržítka a musí začínat písmenem.")),
         Field('lang',
               _("Jazyk"),
               editable=ONCE,
               codebook=self._spec_name('Languages'),
               value_column='lang',
               not_null=True,
               selection_type=pp.SelectionType.CHOICE),
         Field('title_or_identifier',
               _("Title"),
               width=30,
               type=_TreeOrder()),
         Field('title',
               _("Title"),
               width=20,
               type=pd.String(maxlen=32, not_null=True),
               descr=_("Název položky menu - krátký a výstižný.")),
         Field(
             'heading',
             _("Nadpis"),
             width=32,
             type=pd.String(maxlen=40),
             descr=
             _("Hlavní nadpis při zobrazení stránky.  Pokud ponecháte nevyplněné, "
               "použije se název položky.  Ten je ale někdy kvůli použití v menu příliš "
               "krátký, takže zde je možné určit jeho delší variantu.")),
         Field(
             'description',
             _("Description"),
             width=72,
             descr=_(
                 "Stručný popis stránky (zobrazen v menu jako tooltip).")),
         Field(
             'content',
             _("Content"),
             compact=True,
             height=20,
             width=80,
             text_format=pp.TextFormat.LCG,
             attachment_storage=self._attachment_storage,
             descr=
             _("Text stránky formátovaný jako LCG strukturovaný text (wiki)"
               )),
         Field('mod_id',
               _("Modul"),
               type=pd.Integer(),
               not_null=False,
               codebook=self._spec_name('Modules', False),
               allow_codebook_insert=True,
               descr=_(
                   "Vyberte rozšiřující modul zobrazený uvnitř stránky.  "
                   "Ponechte prázdné pro prostou textovou stránku.")),
         Field('modname', _("Modul")),
         Field(
             'parent',
             _("Parent item"),
             type=pd.Integer(),
             not_null=False,
             codebook=self._spec_name('MenuParents', False),
             value_column='menu_item_id',
             runtime_filter=computer(self._parent_filter),
             descr=_(
                 "Vyberte bezprostředně nadřízenou položku v hierarchii menu.  Ponechte "
                 "prázdné pro stránky na nejvyšší úrovni menu.")),
         Field(
             'published',
             _("Zveřejněno"),
             width=6,
             default=True,
             fixed=True,
             descr=_(
                 "Umožňuje řídit dostupnost dané položky nezávisle pro kažou jazykovou "
                 "verzi.")),
         Field(
             'ord',
             _("Ordering"),
             width=8,
             editable=ALWAYS,
             fixed=True,
             descr=
             _("Enter a number denoting the order of the item in menu between "
               "pages of the same hierarchy level.  Leave empty to put the item "
               "automatically to bottom.")),
     )
Exemplo n.º 15
0
Arquivo: misc.py Projeto: cerha/pytis
class Products(Specification):
    public = True
    table = dbdefs.Products
    title = _("Products")
    layout = TabGroup((_("Product"),
                       ('product_id', 'product', 'count', 'price', 'marked')),
                      (_("Notes"),
                       ('since', 'notes')))
    grouping_functions = (
        ('f_date_year', _("Year"), pd.DateTime, pd.Integer()),
        ('f_date_month', _("Month"), pd.DateTime, pd.Integer()),
    )
    profiles = (
        pp.Profile('marked', _("Marked"), filter=pd.EQ('marked', pd.bval(True))),
        pp.Profile('unmarked', _("Unmarked"), filter=pd.EQ('marked', pd.bval(False))),
    )

    def _customize_fields(self, fields):
        fields.set_property('width', product_id=3, product=30, count=12, price=12, notes=50)
        fields.modify('product_id', column_width=6, fixed=True, editable=Editable.ALWAYS)
        fields.modify('product', style=lambda r: (pp.Style(overstrike=True)
                                                  if r['count'].value() == 0
                                                  else None))
        fields.modify('count', style=lambda r: (pp.Style(foreground='#f00')
                                                if r['count'].value() <= 5
                                                else None))
        fields.modify('price', type=pd.Monetary(not_null=True))
        fields.modify('since', descr=_("Date when the product was first included."),
                      default=pd.DateTime.datetime)
        fields.modify('notes', descr=_("Arbitrary internal notes about given product."),
                      height=3, text_format=pp.TextFormat.LCG)

    def prints(self):
        return (
            PrintAction('product-page', _("Product Page"), 'Products.ProductPage',
                        handler=self._print_handler),
            PrintAction('product-info', _("Product Info"), 'Products.ProductInfo'),
        )

    def _print_handler(self, row):
        """Demonstration of using PrintAction handler.

        The output document is created by merging the output of two printout
        results.

        """
        from PyPDF2 import PdfFileMerger, PdfFileReader
        merger = PdfFileMerger()
        for lang in ('cs', 'en'):
            output = io.BytesIO()
            pytis.form.printout(
                'Products', 'Products.ProductPage',
                parameters=dict([(k, row[k].export()) for k in ('product_id', 'product', 'price')],
                                language=lang),
                language=lang, output_file=output,
            )
            merger.append(PdfFileReader(io.BytesIO(output.getvalue())))
        with tempfile.NamedTemporaryFile(suffix='.pdf') as f:
            merger.write(f)
            f.flush()
            os.fsync(f)
            pytis.form.launch_file(f.name)
            time.sleep(1)

    def _content(self, record):
        par = os.path.pardir
        return pytis.util.lcg_node(
            (
                lcg.fieldset((
                    (_("Price"), record['price'].export()),
                    (_("Available since"), lcg.LocalizableDateTime(record['since'].value())),
                )),
                lcg.sec(_("Notes"), lcg.Parser().parse(record['notes'].value()), name='notes')
                if record['notes'].value() else lcg.Content(),
            ),
            title=record['product'].value(),
            resource_path=(os.path.normpath(os.path.join(__file__, par, par, par, par, 'css')),),
            resources=('pytis-demo-product.css',),
        )

    def bindings(self):
        return (
            Binding('webform', _("Product Info"), content=self._content,
                    descr=_("Shows web page generated from LCG structured text.")),
        )

    def actions(self):
        return (Action('toggle', _("Mark/unmark"), self._mark, hotkey='m'),
                Action('mark_all', _("Mark all"), self._mark, hotkey='Ctrl-a', mark_all=True),
                Action('unmark_all', _("Unmark all"), self._mark, hotkey='Ctrl-u', mark_all=True,
                       mark=False),
                Action('mark_selected', _("Mark selected"), self._mark_selected,
                       context=pp.ActionContext.SELECTION),
                Action('prices', _("Update prices"), self._update_prices),
                Action('print', _("Print price"), self._print),
                )

    def row_style(self, row):
        if row['count'].value() <= row.form.query_fields.row['min_count'].value():
            return pp.Style(background='#fdd')
        elif row['marked'].value():
            return pp.Style(background='#ffd')
        else:
            return None

    query_fields = QueryFields(
        (Field('min_count', _("Highlight low count"),
               type=pytis.data.Integer(not_null=True), default=10),),
        autoinit=True,
    )

    def _mark(self, row, mark_all=False, mark=True):
        if mark_all:
            product_id = None
        else:
            product_id = row["product_id"].value()
            mark = not row["marked"].value()
        count = pytis.form.run_procedure('misc', 'mark_products', product_id=product_id, mark=mark)
        app.echo(_("Marked %d rows") % count)

    def _mark_selected(self, rows):
        count = pytis.form.run_procedure('misc', 'mark_products',
                                         product_id=[r['product_id'].value() for r in rows],
                                         mark=True)
        app.echo(_("Marked %d rows") % count)

    def _update_prices(self, row):
        # This serves for testing user transactions.
        true_value = pd.Value(pd.Boolean(), True)
        condition = pd.EQ('marked', true_value)
        transaction = pd.transaction()
        try:
            def process(row):
                return row['product_id']
            product_ids = row.data().select_map(process, condition=condition)
            for product_id in product_ids:
                if not pytis.form.run_form(pytis.form.PopupEditForm, 'misc.Products',
                                           select_row=product_id,
                                           transaction=transaction):
                    app.error("Transaction aborted")
                    transaction.rollback()
                    return
        except Exception:
            transaction.rollback()
            raise
        transaction.commit()

    def _print(self, row):
        pytis.form.printout('misc.Products', 'misc.StandalonePrint', row)
Exemplo n.º 16
0
Arquivo: misc.py Projeto: cerha/pytis
    def fields(self):
        return (
            # Basic fields
            Field('text', _("Text"), type=pd.String(maxlen=50), width=30,
                  editable=computer(self._editable)),
            Field('completion', _("Completion"), type=pd.String(maxlen=20),
                  editable=computer(self._editable),
                  completer=('John', 'Jane', 'Joe', 'Judith', 'Jeremy',
                             'Peter', 'Paul', 'Paula', 'Richard', 'Samantha', 'Samuel',
                             'Steve', 'Sue'),
                  descr=_("Text entry with autocompletion.  The 'completer' provides "
                          "available completions.  As opposed to enumerator (which "
                          "also provides autocompletion values), completer has no "
                          "effect on validation. You can define a static (like here), "
                          "dynamic or database connected list of completions.")),
            Field('multiline', _("Multiline"), type=pd.String(), width=30, height=4,
                  editable=computer(self._editable)),
            Field('password', _("Password"), type=pd.Password(maxlen=20, verify=True),
                  editable=computer(self._editable), width=14,
                  descr=_("Password field doesn't display the current value.  If 'verify' "
                          "is set to True in field's type, the field requires the value to "
                          "be entered twice and validation makes sure both values match.")),
            Field('number', _("Number"), type=pd.Integer(), default=0,
                  editable=computer(self._editable),
                  descr=_("A simple numeric field.")),
            Field('slider', _("Slider"), type=pd.Integer(minimum=20, maximum=50),
                  default=30, slider=True, width=2,
                  editable=computer(self._editable),
                  descr=_("A simple numeric field.")),
            Field('float', _("Decimal"), type=pd.Float(precision=2), default=0,
                  editable=computer(self._editable)),
            Field('date', _("Date"), default=pd.Date.datetime, type=pd.Date(),
                  editable=computer(self._editable),
                  descr=_("Date entry field with a calendar control and a default value.")),
            Field('time', _("Time"), type=pytis.data.Time(),
                  editable=computer(self._editable)),
            Field('datetime', _("Date and time"), type=pytis.data.DateTime(),
                  editable=computer(self._editable)),
            Field('file', _("File"), type=pytis.data.Binary(),
                  editable=computer(self._editable)),
            Field('image', _("Image"), type=pytis.data.Image(),
                  editable=computer(self._editable)),
            Field('color', _("Color"), type=pytis.data.Color(),
                  editable=computer(self._editable)),

            # Enumeration fields
            Field('editable', _("Check box"), type=pd.Boolean(), default=True,
                  descr=_("Check/uncheck this checkbox to enable/disable all other "
                          "form fields.  This field demonstrates a simple boolean "
                          "checkbox field.  It also demonstrates how to make fields "
                          "editable based on the values of some other fields (any "
                          "more complicated logic is possible in the 'computer' of "
                          "the 'editable' property in field specification). "
                          "Finally it demonstrates how all field types look in the "
                          "editable and non-editable state.")),
            Field('choice', _("Choice"), type=pd.String(not_null=True, maxlen=2),
                  codebook='cb.Continents',
                  selection_type=SelectionType.CHOICE,
                  editable=computer(self._editable),
                  descr=_("Simple pull-down menu selection.")),
            Field('radio', _("Radio box"), not_null=True,
                  enumerator=self._Fruits,
                  editable=computer(self._editable),
                  selection_type=SelectionType.RADIO,
                  descr=_("Radio box selection.  The enumeration is defined statically by "
                          "an Enumeration class.")),
            Field('listbox', _("List box"), not_null=True,
                  enumerator=self._Fruits,
                  selection_type=SelectionType.LISTBOX,
                  editable=computer(self._editable),
                  descr=_("List selection which becomes scrollable when the number of "
                          "items exceeds its height.")),
            Field('codebook', _("Codebook"), type=pd.String(not_null=True, maxlen=2),
                  codebook='cb.Countries', width=2,
                  # SelectionType.CODEBOOK is actually the default when 'codebook' is set.
                  selection_type=SelectionType.CODEBOOK,
                  post_process=PostProcess.UPPER,
                  editable=computer(self._editable),
                  descr=_(
                      "This field provides a text entry for writing down the code directly.  "
                      "It most convenient when the codebook contains a relatively large number of "
                      "items while their codes are short and simple.  Then the user most often "
                      "knows the codes without looking them up.  Right from the text entry field, "
                      "there is a codebook invocation button, which allows the user to select the "
                      "item from a codebook form (when he doesn't remember the code or wants to "
                      "see the full listing of all available items for some other reason).  "
                      "Further on the right, there is a gray field called `display'.  It shows "
                      "the description of the currently selected item (eg. the full name for the "
                      "selected code).  Thus it provides immediate feedback when the user "
                      "modifies the value.  It is empty when the entered code is invalid (not "
                      "contained in the codebook).  It is updated with any change of the field "
                      "value.")),
            Field('listfield', _("List Field"), type=pd.String(not_null=True, maxlen=2),
                  codebook='cb.Countries', compact=True, height=6,
                  selection_type=SelectionType.LIST,
                  editable=computer(self._editable),
                  descr=_("This field also allows direct codebook manipulations through the row "
                          "context menu.")),

            # Range fields
            Field('intrange', _("Numeric range"), type=pytis.data.IntegerRange(),
                  editable=computer(self._editable)),
            Field('daterange', _("Date range"), type=pytis.data.DateRange(),
                  editable=computer(self._editable)),

        )
Exemplo n.º 17
0
class CryptoKeys(CMSExtensionModule):
    """Management of keys and users.

    It uses a special set of actions that resemble standard editing actions but
    are different from them:

    - Insert new key: This creates initial key for an encryption area.  This
      can be done only once for each of the areas.  The inserted keys can be
      given to other users using copy action.

    - Copy key: Copy an existing key to another user.

    - Change password: Change password of the key for the given user.

    - Delete key: Remove a user from the given key.  This can be only done if
      another copy of the key exists.

    All the actions are performed using special database functions.

    """
    class Spec(wiking.Specification):
        table = 'cms_crypto_keys'
        title = _("Users and Encryption Keys")

        def fields(self):
            return (
                Field('key_id', _("Id"), editable=Editable.NEVER),
                Field('name',
                      _("Name"),
                      not_null=True,
                      codebook='CryptoNames',
                      editable=Editable.NEVER),
                Field('uid',
                      _("User"),
                      not_null=True,
                      codebook='Users',
                      selection_type=SelectionType.CHOICE,
                      editable=Editable.ONCE),
                Field('new_uid',
                      _("New user"),
                      not_null=True,
                      codebook='Users',
                      selection_type=SelectionType.CHOICE,
                      type=pd.Integer,
                      virtual=True,
                      runtime_filter=computer(self._new_uid_filter)),
                Field('key', _("Key")),
                Field('remove',
                      _("Action"),
                      virtual=True,
                      computer=computer(lambda r: _("Remove"))),
                Field('old_password',
                      _("Current password"),
                      type=pd.Password,
                      verify=False,
                      virtual=True),
                Field('new_password',
                      _("New password"),
                      type=pd.Password,
                      virtual=True),
                Field('delete',
                      virtual=True,
                      computer=computer(lambda row: _("Remove"))),
            )

        def _new_uid_filter(self, row, name):
            assigned_users = wiking.module(self._resolver).assigned_users(
                row['name'])
            return pd.AND(*[pd.NE('uid', u) for u in assigned_users])

        sorting = (
            (
                'uid',
                pd.ASCENDENT,
            ),
            (
                'name',
                pd.ASCENDENT,
            ),
        )
        columns = (
            'uid',
            'name',
            'delete',
        )
        actions = (
            Action('password',
                   _("Change password"),
                   descr=_("Change key password")),
            Action('adduser',
                   _("Copy to user"),
                   descr=_("Add another user of the key")),
        )

    _DB_FUNCTIONS = dict(
        CMSExtensionModule._DB_FUNCTIONS,
        cms_crypto_delete_key=(
            (
                'name_',
                pd.String(),
            ),
            (
                'uid_',
                pd.Integer(),
            ),
            (
                'force',
                pd.Boolean(),
            ),
        ),
        cms_crypto_insert_key=(
            (
                'name_',
                pd.String(),
            ),
            (
                'uid_',
                pd.Integer(),
            ),
            (
                'key_',
                pd.String(),
            ),
            (
                'psw',
                pd.String(),
            ),
        ),
        cms_crypto_copy_key=(
            (
                'name_',
                pd.String(),
            ),
            (
                'from_uid',
                pd.Integer(),
            ),
            (
                'to_uid',
                pd.Integer(),
            ),
            (
                'from_psw',
                pd.String(),
            ),
            (
                'to_psw',
                pd.String(),
            ),
        ),
        cms_crypto_change_password=(
            (
                'id_',
                pd.Integer(),
            ),
            (
                'old_psw',
                pd.String(),
            ),
            (
                'new_psw',
                pd.String(),
            ),
        ),
    )

    _TITLE_COLUMN = 'uid'
    _INSERT_LABEL = _("Create key")

    def _authorized(self, req, action, record=None, **kwargs):
        if action == 'view':
            return req.check_roles(Roles.CRYPTO_ADMIN) or self._check_uid(
                req, record, 'uid')
        elif action == 'password':
            return self._check_uid(req, record, 'uid')
        elif action in ('list', 'insert', 'delete', 'adduser'):
            return req.check_roles(Roles.CRYPTO_ADMIN)
        else:
            return False

    def _prefill(self, req):
        return dict(super(CryptoKeys, self)._prefill(req),
                    uid=req.user().uid())

    def _layout(self, req, action, record=None):
        if action == 'insert':
            layout = (
                'name',
                'uid',
                'new_password',
            )
        elif action == 'adduser':
            layout = (
                'key_id',
                'name',
                'uid',
                'new_uid',
                'old_password',
                'new_password',
            )
        elif action == 'password':
            layout = (
                'key_id',
                'name',
                'uid',
                'old_password',
                'new_password',
            )
        else:
            layout = (
                'name',
                'uid',
            )
        return layout

    def _columns(self, req):
        columns = super(CryptoKeys, self)._columns(req)
        if not req.has_param('_crypto_name'):
            columns = [c for c in columns if c != 'delete']
        return columns

    def _link_provider(self, req, uri, record, cid, **kwargs):
        if cid == 'delete':
            return req.make_uri(uri,
                                key_id=record['key_id'].value(),
                                action='delete')
        else:
            return super(CryptoKeys,
                         self)._link_provider(req, uri, record, cid, **kwargs)

    def _list_form_kwargs(self, req, form_cls):
        kwargs = super(CryptoKeys, self)._list_form_kwargs(req, form_cls)
        if issubclass(form_cls, pw.ItemizedView) and req.check_roles(
                Roles.USER_ADMIN):
            kwargs['template'] = lcg.TranslatableText("%%(%s)s [%%(delete)s]" %
                                                      self._TITLE_COLUMN)
        return kwargs

    def related(self, req, binding, record, uri):
        if 'name' in record:
            req.set_param('_crypto_name', record['name'])
        return super(CryptoKeys, self).related(req, binding, record, uri)

    def _actions(self, req, record):
        actions = super(CryptoKeys, self)._actions(req, record)
        if record is None and req.has_param('_crypto_name'):
            condition = pd.EQ('name', req.param('_crypto_name'))
            try:
                count = self._data.select(condition)
            finally:
                try:
                    self._data.close()
                except Exception:
                    pass
            if count > 0:
                actions = [a for a in actions if a.id() != 'insert']
        return actions

    def _insert(self, req, record, transaction):
        key = wiking.generate_random_string(256)
        if not self._call_db_function('cms_crypto_insert_key',
                                      record['name'].value(),
                                      record['uid'].value(),
                                      key,
                                      record['new_password'].value(),
                                      transaction=transaction):
            raise pd.DBException(
                _("New key not created. Maybe it already exists?"))

    def _update(self, req, record, transaction):
        action = req.param('action')
        if action == 'password':
            if not self._call_db_function('cms_crypto_change_password',
                                          record['key_id'].value(),
                                          record['old_password'].value(),
                                          record['new_password'].value(),
                                          transaction=transaction):
                raise pd.DBException(
                    _("Password not changed. Maybe invalid old password?"))
        elif action == 'adduser':
            if not self._call_db_function('cms_crypto_copy_key',
                                          record['name'].value(),
                                          record['uid'].value(),
                                          record['new_uid'].value(),
                                          record['old_password'].value(),
                                          record['new_password'].value(),
                                          transaction=transaction):
                raise pd.DBException(
                    _("User not added. Maybe invalid old password?"))
        else:
            raise Exception('Unexpected action', action)

    def _delete(self, req, record, transaction):
        if not self._call_db_function('cms_crypto_delete_key',
                                      record['name'].value(),
                                      record['uid'].value(),
                                      False,
                                      transaction=transaction):
            raise pd.DBException(
                _("The user couldn't be deleted. "
                  "Maybe he is the last key holder?"))

    def action_adduser(self, req, record, action='adduser'):
        return super(CryptoKeys, self).action_update(req,
                                                     record,
                                                     action=action)

    def action_password(self, req, record=None):
        return self.action_update(req, record=record, action='password')

    def assigned_users(self, name):
        # Internal method for Spec class, don't use it elsewhere
        return self._data.select_map(lambda row: row['uid'],
                                     condition=pd.EQ('name', name))

    def assigned_names(self, uid):
        """Return sequence of crypto names assigned to user identified by uid.

        Arguments:

          uid -- user uid, integer

        """
        return self._data.select_map(lambda row: row['name'],
                                     condition=pd.EQ('uid', pd.ival(uid)))

    def clear_crypto_passwords(self, req, user):
        # Just a hack to allow clearing passwords on logout
        req.set_cookie(self._CRYPTO_COOKIE, None, secure=True)
        self._call_db_function('cms_crypto_lock_passwords', user.uid())
Exemplo n.º 18
0
Arquivo: test.py Projeto: cerha/pytis
class Timezone(datetime.tzinfo):
    _ZERO_DIFF = datetime.timedelta(0)

    def utcoffset(self, dt):
        return self._ZERO_DIFF

    def tzname(self, dt):
        return "XXX"

    def dst(self, dt):
        return self._ZERO_DIFF


tests = (
    (pp.Field('numeric', type=pd.Integer()), (5, '5')),
    (pp.Field('string', type=pd.String()), ('x', 'x')),
    (pp.Field('multiline', type=pd.String(),
              height=4), ('xxx\nxxx', 'xxx\nxxx')),
    (pp.Field('date', type=pd.Date()), (datetime.date(2016, 8,
                                                      30), '30.08.2016')),
    (pp.Field('datetime',
              type=pd.DateTime()), (datetime.datetime(2016,
                                                      8,
                                                      30,
                                                      12,
                                                      40,
                                                      tzinfo=Timezone()),
                                    '30.08.2016 12:40:00')),
    (pp.Field('daterange', type=pd.DateRange()), (pd.DateRange().adjust_value(
        (datetime.date(1975, 8, 30),
Exemplo n.º 19
0
 def _action_filter(self, record, mod_id):
     return pd.OR(pd.EQ('mod_id', record['mod_id']),
                  pd.EQ('mod_id', pd.Value(pd.Integer(), None)))
Exemplo n.º 20
0
 def close(self, req, user, session_key):
     # This deletion will lead to end_time in cms_session_log_data being set to last_access
     # value of the deleted row.  Use delete_many() because we don't know session_id.
     self._data.delete_many(
         pd.AND(pd.EQ('uid', pd.Value(pd.Integer(), user.uid())),
                pd.EQ('session_key', pd.Value(pd.DateTime(), session_key))))