Exemple #1
0
def index():
    tax = db.product.price * 5 / 100
    tax.represent = db.product.price.represent
    pretax_price = db.product.price + tax
    pretax_price.represent = db.product.price.represent

    request_headers = request.vars.headers or 'default'
    request_columns = request.vars.columns or 'default'

    ################################ The core ######################################
    # A custom orderby selector for the solidtable.
    orderby_selector = OrderbySelector([
        db.product.id, db.product.name, ~db.product.publish_start_date,
        ~pretax_price
    ])

    rows = db().select(db.product.ALL,
                       tax,
                       pretax_price,
                       orderby=orderby_selector.orderby())

    if request_headers == 'default':
        # The "headers" is a dictionary of dictionaries for updating default values.
        # Custom fields such as "tax" can be passed to the dictionary keys.
        headers = {
            'product.name': {
                'selected': True
            },
            'product.description': {
                'label': 'Details',
                'class': 'italic',
                'width': '200px',
                'truncate': 38,
            },
            tax: {
                'label': 'Tax'
            },
            pretax_price: {
                'label': 'Pretax Price'
            }
        }
    else:
        # The "headers" can be 'labels' or 'fieldname:capitalize'
        headers = request_headers

    extracolumns = [
        {
            'label': A('Edit', _href='#'),
            'content':
            lambda row, rc: A('Edit', _href='edit/%s' % row.product.id)
        },
        {
            'label':
            A('Delete', _href='#'),
            'content':
            lambda row, rc: A('Delete', _href='delete/%s' % row.product.id)
        },
    ]

    # The structure of "columns" defines the multi-line table layout
    # A "None" indicates an empty line over which the precedent line spans
    # Custom fields such as "tax" and extracolumns can be passed to the list of lists.
    if request_columns == 'default':
        columns = [
            [db.product.name, extracolumns[0]],
            'product.id',
            ['product.status', 'product.publish_start_date'],
            [None, 'product.publish_end_date'],
            [pretax_price, 'product.description'],
            [tax, None],
            ['product.price', None],
        ]
    elif request_columns == 'columns_2':
        columns = [
            [db.product.name, extracolumns[0]],
            None,
            [pretax_price, 'product.description'],
            None,
            [tax, None],
        ]
    elif request_columns == 'columns_3':
        columns = [
            [db.product.name, extracolumns[0]],
            [None, 'product.description'],
            [None, tax],
        ]

    table = SOLIDTABLE(rows,
                       columns=columns,
                       extracolumns=extracolumns,
                       headers=headers,
                       orderby=orderby_selector,
                       renderstyle=True,
                       linkto=URL('show'),
                       selectid=lambda r: r.product.id == 7)
    ################################################################################

    return dict(table=DIV(table, STYLE('.italic {font-style: italic;}')),
                table_args=DIV(
                    A('headers=default',
                      _href=URL(vars={'headers': 'default'})),
                    ' ',
                    A('headers=labels', _href=URL(vars={'headers': 'labels'})),
                    ' ',
                    A('headers=fieldname:capitalize',
                      _href=URL(vars={'headers': 'fieldname:capitalize'})),
                    ' ',
                    A('columns=default',
                      _href=URL(vars={'columns': 'default'})),
                    ' ',
                    A('columns=columns_2',
                      _href=URL(vars={'columns': 'columns_2'})),
                    ' ',
                    A('columns=columns_3',
                      _href=URL(vars={'columns': 'columns_3'})),
                    ' ',
                ))
Exemple #2
0
    def __call__(
            self,
            query,
            fields=None,
            field_id=None,
            left=None,
            headers={},
            columns=None,
            orderby=None,  # EXTENDED for permutation
            searchable=True,  # EXTENDED ex) [table.id, table.name, ...]
            sortable=True,  # EXTENDED ex) [table.id, table.name, ...]
            paginate=(10, 25, 50, 100),  # EXTENDED
            deletable=True,
            editable=True,  # EXTENDED ex) [['id', 'name'], 'profile', ...]
            details=True,  # EXTENDED ex) [['id', 'name'], 'profile', ...]
            selectable=None,  # TODO 
            create=True,  # EXTENDED ex) [['id', 'name'], 'profile', ...]
            csv=True,
            links=None,
            upload='<default>',
            args=[],
            user_signature=True,
            maxtextlengths={},  # NOT WORK
            maxtextlength=20,
            onvalidation=None,
            oncreate=None,
            onupdate=None,
            ondelete=None,
            sorter_icons=('[^]', '[v]'),  # NOT WORK
            ui='ui',  # ONLY WORK FOR "ui"
            showbuttontext=True,
            _class="web2py_grid",
            formname='web2py_grid',
            search_widget='default',  # NOT WORK
            extracolumns=None,  # CUSTOM (same as in SQLTABLE)
            search_queries={},  # CUSTOM
            showid=True,  # CUSTOM
            onpermute=None,  # CUSTOM
            virtualtable=None,  # CUSTOM
            virtualrecord=None,  # CUSTOM
            virtualset=None,  # CUSTOM
            hmac_key=None,  # CUSTOM
            scope=None,  #CUSTOM
            scope_default=None,  #CUSTOM
            groupby=None,  #CUSTOM
    ):

        from gluon.dal import SQLALL
        from plugin_solidform import SOLIDFORM
        from plugin_solidtable import SOLIDTABLE, OrderbySelector
        from plugin_paginator import Paginator, PaginateSelector, PaginateInfo
        gridbutton = self.gridbutton
        recordbutton = self.recordbutton

        request, session, T = current.request, current.session, current.T

        def __oncreate(form):
            session.flash = T('Created')

        def __onupdate(form):
            session.flash = T('Updated')

        def __ondelete(table, tablename, ret):
            session.flash = T('Deleted')

        def __onpermute(table, tablename, ret):
            session.flash = T('Permuted')

        def redirect_patch():
            onupdate

        oncreate = oncreate or __oncreate
        onupdate = onupdate or __onupdate
        ondelete = ondelete or __ondelete
        onpermute = onpermute or __onpermute

        if ui == 'ui':
            ui = dict(
                widget='',
                header='',
                content='',
                default='',
                cornerall='',
                cornertop='',
                cornerbottom='',
                button='',
                buttontext='',
                buttonadd='ui-icon-plusthick',
                buttonback='ui-icon-arrowreturnthick-1-w',
                buttonexport='',
                buttondelete='ui-icon-close',
                buttonedit='ui-icon-pencil',
                buttontable='',
                buttonview='ui-icon-zoomin',
            )
        elif not isinstance(ui, dict):
            raise RuntimeError, 'SQLFORM.grid ui argument must be a dictionary'

        wenabled = (not user_signature or (session.auth and session.auth.user))
        deletable = wenabled and deletable
        # if search_widget=='default':
        # search_widget = SQLFORM.search_menu

        url = self.url_factory(args, user_signature, hmac_key)

        db = query._db
        dbset = db(query)
        tables = [
            db[tablename] for tablename in db._adapter.tables(dbset.query)
        ]
        if not fields:
            fields = reduce(lambda a, b: a + b,
                            [[field for field in table] for table in tables])

        new_fields = []
        for item in fields:
            if isinstance(item, SQLALL):
                new_fields += item.table
            else:
                new_fields.append(item)
        fields = new_fields

        main_table = tables[0]
        if not field_id:
            field_id = main_table._id

        table = field_id.table
        tablename = table._tablename
        referrer = session.get('_web2py_grid_referrer_' + formname, url())

        def __from_process_redirect_patch(func):
            def wrapper(form):
                func(form)
                redirect(referrer)

            return wrapper

        oncreate = __from_process_redirect_patch(oncreate)
        onupdate = __from_process_redirect_patch(onupdate)

        def check_authorization():
            if user_signature or hmac_key:
                if not URL.verify(request,
                                  user_signature=user_signature,
                                  hmac_key=hmac_key):
                    session.flash = T('not authorized')
                    redirect(referrer)

        if upload == '<default>':
            upload = lambda filename: url(args=['download', filename])
            if len(request.args) > 1 and request.args[-2] == 'download':
                check_authorization()
                stream = response.download(request, db)
                raise HTTP(200, stream, **response.headers)

        gridbuttons = [gridbutton('%(buttonback)s' % ui, T('Back'), referrer)]

        if create and len(request.args) > 1 and request.args[-2] == 'new':
            check_authorization()
            table = db[request.args[-1]]
            self.mark_not_empty(virtualtable or table)
            if orderby:
                inverted = (orderby.op == orderby.db._adapter.INVERT)
                field = orderby.first if inverted else orderby
                last = dbset.select(
                    field_id,
                    field,
                    limitby=(0, 1),
                    orderby=orderby.first if inverted else ~orderby).first()
                last_value = (last[field] or 0) if last else 0
                table[field.name].default = (-1
                                             if inverted else 1) + last_value

            create_form = SOLIDFORM(
                virtualtable or table,
                fields=create if type(create) in (list, tuple) else None,
                showid=showid,
                _class='web2py_form',
                submit_button=T('Create'),
            ).process(  # next=referrer, for web2py-bug
                onvalidation=onvalidation,
                onsuccess=oncreate,
                formname=formname)
            self.unmark_not_empty(table)
            res = DIV(create_form, _class=_class)
            res.create_form = create_form
            res.gridbuttons = gridbuttons
            return res

        elif details and len(request.args) > 2 and request.args[-3] == 'view':
            check_authorization()
            table = db[request.args[-2]]
            record = table(request.args[-1]) or redirect(URL('error'))
            form = SOLIDFORM(virtualtable or table,
                             virtualrecord or record,
                             fields=details if type(details) in (list, tuple)
                             else create if type(create) in (list,
                                                             tuple) else None,
                             upload=upload,
                             readonly=True,
                             showid=showid,
                             _class='web2py_form')
            res = DIV(form, _class=_class)
            res.record = record  # CUSTOM
            res.view_form = form  # CUSTOM
            if editable:
                gridbuttons.append(
                    gridbutton('%(buttonedit)s' % ui, T('Edit'),
                               url(args=['edit', tablename, record.id])))
            res.gridbuttons = gridbuttons
            return res

        elif editable and len(request.args) > 2 and request.args[-3] == 'edit':
            check_authorization()
            table = db[request.args[-2]]
            record = table(request.args[-1]) or redirect(URL('error'))
            self.mark_not_empty(virtualtable or table)
            edit_form = SOLIDFORM(
                virtualtable or table,
                virtualrecord or record,
                fields=editable if type(editable) in (list, tuple) else
                create if type(create) in (list, tuple) else None,
                upload=upload,
                deletable=deletable,
                showid=showid,
                delete_label=T('Check to delete:'),
                submit_button=T('Update'),
                _class='web2py_form')
            self.unmark_not_empty(table)
            edit_form.process(
                formname=formname,
                onvalidation=onvalidation,
                onsuccess=onupdate,
                # #next=referrer, for web2py-bug
            )
            res = DIV(edit_form, _class=_class)
            res.record = record  # CUSTOM
            res.edit_form = edit_form
            if details:
                gridbuttons.append(
                    gridbutton('%(buttonview)s' % ui, T('View'),
                               url(args=['view', tablename, record.id])))
            res.gridbuttons = gridbuttons
            return res

        elif deletable and len(
                request.args) > 2 and request.args[-3] == 'delete':
            check_authorization()
            table = db[request.args[-2]]
            ret = db(table.id == request.args[-1]).delete()
            if ondelete:
                ondelete(table, request.args[-1], ret)
            redirect(url())

        elif csv and len(request.args) > 0 and request.args[-1] == 'csv':
            check_authorization()
            return dict()

        elif request.vars.records and not isinstance(request.vars.records,
                                                     list):
            request.vars.records = [request.vars.records]

        elif not request.vars.records:
            request.vars.records = []

        session['_web2py_grid_referrer_' + formname] = URL(
            r=request,
            args=request.args,
            vars=request.vars,
            user_signature=user_signature,
            hmac_key=hmac_key)

        error = None
        search_form = None
        table_el_id = formname + '_maintable'

        columns = columns or [
            str(f) for f in fields if f.table == main_table and f.readable and
            (showid or f.type != 'id')
        ]

        if searchable:
            field_sep = '___'

            if searchable is True:
                _exclude_types = ('upload',
                                  'text') if showid else ('id', 'upload',
                                                          'text')
                searchable = [
                    f for f in fields if f.table == main_table
                    and f.type not in _exclude_types and f.readable
                ]

            _search_fields = []
            _from_tos = []
            for f in searchable:
                _requires = []
                if f.requires and type(f.requires) not in (list, tuple):
                    if isinstance(f.requires, IS_EMPTY_OR):
                        _requires = [f.requires.other]
                    else:
                        _requires = [f.requires]
                _requires = [
                    r for r in _requires if not isinstance(r, IS_NOT_IN_DB)
                ]
                if _requires:
                    if len(_requires) == 1:
                        _requires = _requires[0]
                    _requires = IS_EMPTY_OR(_requires)
                else:
                    _requires = None

                _type = 'string' if f.type == 'text' else 'integer' if f.type == 'id' else f.type

                if (f.type in ('double', 'decimal', 'date', 'datetime')
                        or (f.type == 'integer' and _requires
                            and isinstance(_requires.other,
                                           (IS_INT_IN_RANGE)))):
                    _from_to = [
                        Field(str(f).replace('.', field_sep) + field_sep +
                              'from',
                              type=_type,
                              requires=_requires,
                              label=f.label,
                              widget=f.widget),
                        Field(str(f).replace('.', field_sep) + field_sep +
                              'to',
                              type=_type,
                              requires=_requires,
                              label=f.label,
                              widget=f.widget)
                    ]
                    _from_tos.append(_from_to)
                    _search_fields += _from_to
                elif hasattr(f, 'table'):
                    _search_fields.append(
                        Field(str(f).replace('.', field_sep),
                              type=_type,
                              requires=_requires,
                              label=f.label,
                              widget=f.widget))
                else:
                    _search_fields.append(f)

            search_form = SQLFORM.factory(formstyle='divs',
                                          submit_button=T('Search'),
                                          _class='search_form',
                                          *_search_fields)

            for _from_to in _from_tos:
                self.inline(search_form,
                            'no_table', [f.name for f in _from_to],
                            LABEL(_from_to[0].label), SPAN(' - '))

            subquery = self.build_query_by_form(db,
                                                search_form,
                                                queries=search_queries,
                                                field_sep=field_sep,
                                                formname='search_%s' %
                                                formname)
        else:
            subquery = None

        if subquery:
            dbset = dbset(subquery)

        if scope:
            from plugin_tablescope import TableScope
            scope_el = TableScope(dbset, scope, default=scope_default)
            dbset = scope_el.scoped_dataset

        if sortable is True:
            sortable = [
                ~f if f.type in ('id', 'date', 'datetime') else f
                for f in fields
                if f.table == main_table and f.type not in ('text', 'upload')
            ]
        if not sortable:
            sortable = []
        if orderby:
            sortable.insert(0, orderby)

        orderby_selector = OrderbySelector(sortable)

        current_orderby = orderby_selector.orderby()
        permutable = (orderby and (not subquery) and sortable
                      and current_orderby is sortable[0])

        extracolumns = extracolumns or []

        if permutable:
            if len(request.args) > 2 and request.args[-3] in ('up', 'down'):
                check_authorization()
                table = db[request.args[-2]]
                record = table(request.args[-1]) or redirect(URL('error'))
                inverted = (orderby.op == orderby.db._adapter.INVERT)
                field = orderby.first if inverted else orderby

                current_value = record[field]
                if current_value is None:
                    first = dbset.select(field_id,
                                         limitby=(0, 1),
                                         orderby=orderby).first()
                    current_value = (1 if inverted else -1) + (first.id
                                                               if first else 0)

                if (request.args[-3] == ('down' if inverted else 'up')):
                    target = dbset(field < current_value).select(
                        field_id,
                        field,
                        limitby=(0, 1),
                        orderby=orderby if inverted else ~orderby).first()
                elif (request.args[-3] == ('up' if inverted else 'down')):
                    target = dbset(field > current_value).select(
                        field_id,
                        field,
                        limitby=(0, 1),
                        orderby=orderby.first
                        if inverted else orderby).first()
                else:
                    raise NotImplementedError
                if not target:
                    last = dbset.select(field_id,
                                        limitby=(0, 1),
                                        orderby=orderby.first if orderby.first
                                        else ~orderby).first()
                    target_value = (-1 if inverted else 1) + (last.id
                                                              if last else 0)
                else:
                    target_value = target[field]

                db(table.id == record[field_id]).update(
                    **{field.name: target_value})
                if target:
                    db(table.id == target[field_id]).update(
                        **{field.name: current_value})

                if onpermute:
                    onpermute(table, request.args[-2], (record, target))
                redirect(url())

            first = dbset.select(field_id, limitby=(0, 1),
                                 orderby=orderby).first()
            first_id = first.id if first else 0
            last = dbset.select(
                field_id,
                limitby=(0, 1),
                orderby=orderby.first if orderby.first else ~orderby).first()
            last_id = last.id if last else 0
            extracolumns.append({
                'label':
                DIV(T('Move'), _style='text-align:center;'),
                'width':
                '150px' if showbuttontext else '65px',
                'content':
                lambda row, rc: DIV(
                    recordbutton('ui-icon-triangle-1-n', T('Up'),
                                 url(args=['up', tablename, row[field_id]]),
                                 showbuttontext)
                    if row[field_id] != first_id else '',
                    recordbutton('ui-icon-triangle-1-s', T('Down'),
                                 url(args=['down', tablename, row[field_id]]),
                                 showbuttontext)
                    if row[field_id] != last_id else '',
                    _style='text-align:center;')
            })

        _size = 80 if showbuttontext else 25
        _size = _size * (int(bool(details)) + int(bool(editable)) +
                         int(bool(deletable)))
        if _size:

            extracolumns.append({
                'label':
                '',
                'width':
                '%spx' % (_size + 12),
                'content':
                lambda row, rc: DIV(
                    recordbutton('%(buttonview)s' % ui, T('View'),
                                 url(args=['view', tablename, row[field_id]]),
                                 showbuttontext) if details else '',
                    recordbutton('%(buttonedit)s' % ui, T('Edit'),
                                 url(args=['edit', tablename, row[field_id]]),
                                 showbuttontext) if editable else '',
                    recordbutton(
                        '%(buttondelete)s' % ui,
                        T('Delete'),
                        url(args=['delete', tablename, row[field_id]]),
                        showbuttontext,
                        _onclick="""
if(confirm("%s")){return true;} else {jQuery(this).unbind('click').fadeOut();return false;}"""
                        % T('Sure you want to delete them?'),
                    ) if deletable else '')
            })

        if paginate:
            paginate_selector = PaginateSelector(paginate if type(paginate) in
                                                 (list, tuple) else [paginate])
            current_paginate = paginate_selector.paginate
            paginator = Paginator(paginate=current_paginate)
            # TODO for groupby
            paginator.records = virtualset(
                dbset.query).count() if virtualset else dbset.count()
            paginate_info = PaginateInfo(paginator.page, paginator.paginate,
                                         paginator.records)
            limitby = paginator.limitby()
        else:
            limitby = None
            current_paginate = None

        # TODO
        # if paginator.records == 0:
        # error = 'Not Found'
        if virtualset:
            records = virtualset(dbset.query).select(left=left,
                                                     limitby=limitby,
                                                     orderby=current_orderby,
                                                     groupby=groupby,
                                                     *fields)
            records.db = virtualtable._db
        else:
            records = dbset.select(left=left,
                                   limitby=limitby,
                                   orderby=current_orderby,
                                   groupby=groupby,
                                   *fields)

        table = SOLIDTABLE(
            records,
            columns=columns,
            headers=headers,
            orderby=orderby_selector,
            truncate=maxtextlength,  #TODO replace
            extracolumns=extracolumns,
            upload=upload)
        table.attributes['_class'] = 'solidtable'
        table.attributes['_id'] = table_el_id

        inner = []
        if scope:
            inner.append(scope_el)
        if current_paginate:
            inner.append(DIV(paginate_info, _class='pagination_information'))
        inner.append(table)
        if current_paginate and paginator.records > current_paginate:
            inner.append(
                DIV(paginate_selector, paginator, _class='index_footer'))

        res = DIV(_class=_class, *inner)

        res.records = records
        res.search_form = search_form
        res.error = error
        res.gridbuttons = []
        if create:
            res.gridbuttons.append(
                gridbutton('%(buttonadd)s' % ui, T('Add'),
                           url(args=['new', tablename])))

        return res