Пример #1
0
 def ext_options(self, **kw):
     kw = Component.ext_options(self, **kw)
     # edge case of invalid extjs layout when user doesn't have permision to view vflex items. Ticket #2916
     if getattr(self, 'vflex', False) and kw.get('region', "") != "center" and \
             get_user_profile() and \
             len([e for e in getattr(getattr(self, 'parent',None), "elements",[])
                  if e.get_view_permission(get_user_profile())]) == 1:
         kw['region'] = 'center'
     return kw
Пример #2
0
 def ext_options(self, **kw):
     kw = Component.ext_options(self, **kw)
     # edge case of invalid extjs layout when user doesn't have permision to view vflex items. Ticket #2916
     if getattr(self, 'vflex', False) and kw.get('region', "") != "center" and \
             get_user_profile() and \
             len([e for e in getattr(getattr(self, 'parent',None), "elements",[])
                  if e.get_view_permission(get_user_profile())]) == 1:
         kw['region'] = 'center'
     return kw
Пример #3
0
def get_view_permission(e):
    if isinstance(
            e, Permittable) and not e.get_view_permission(get_user_profile()):
        return False
    # e.g. pcsw.ClientDetail has a tab "Other", visible only to system
    # admins but the "Other" contains a GridElement RolesByPerson
    # which is not per se reserved for system admins.  js of normal
    # users should not try to call on_master_changed() on it
    parent = e.parent
    while parent is not None:
        if isinstance(parent, Permittable) and not parent.get_view_permission(
                get_user_profile()):
            return False  # bug 3 (bcss_summary) blog/2012/0927
        parent = parent.parent
    return True
Пример #4
0
def get_view_permission(e):
    if isinstance(e, Permittable) and not e.get_view_permission(
            get_user_profile()):
        return False
    # e.g. pcsw.ClientDetail has a tab "Other", visible only to system
    # admins but the "Other" contains a GridElement RolesByPerson
    # which is not per se reserved for system admins.  js of normal
    # users should not try to call on_master_changed() on it
    parent = e.parent
    while parent is not None:
        if isinstance(parent, Permittable) and not parent.get_view_permission(
                get_user_profile()):
            return False  # bug 3 (bcss_summary) blog/2012/0927
        parent = parent.parent
    return True
Пример #5
0
    def write_lino_js(self, f):
        """

        :param f: File object
        :return: 1
        """
        raise NotImplementedError("Need to implement a lino_web.js writing script")

        user_type = get_user_profile()

        context = dict(
            ext_renderer=self,
            site=settings.SITE,
            settings=settings,
            lino=lino,
            language=translation.get_language(),
            # ext_requests=constants,
            constants=constants,
            extjs=self.plugin,  # 20171227
        )

        context.update(_=_)

        tpl = self.linolib_template()

        f.write(tpl.render(**context) + '\n')

        return 1
Пример #6
0
 def lino_js_parts(self):
     user_type = get_user_profile()
     filename = 'lino_'
     if user_type is not None:
         filename += user_type.value + '_'
     filename += translation.get_language() + '.js'
     return ('cache', 'js', filename)
Пример #7
0
    def write_lino_js(self, f):
        """

        :param f: File object
        :return: 1
        """
        raise NotImplementedError(
            "Need to implement a lino_web.js writing script")

        user_type = get_user_profile()

        context = dict(
            ext_renderer=self,
            site=settings.SITE,
            settings=settings,
            lino=lino,
            language=translation.get_language(),
            # ext_requests=constants,
            constants=constants,
            extjs=self.plugin,  # 20171227
        )

        context.update(_=_)

        tpl = self.linolib_template()

        f.write(tpl.render(**context) + '\n')

        return 1
Пример #8
0
 def lino_js_parts(self):
     user_type = get_user_profile()
     filename = 'lino_'
     file_type = self.lino_web_template.rsplit(".")[-1]
     if user_type is not None:
         filename += user_type.value + '_'
     filename += translation.get_language() + '.' + file_type
     return ('cache', file_type, filename)
Пример #9
0
 def lino_js_parts(self):
     user_type = get_user_profile()
     filename = 'lino_'
     file_type = self.lino_web_template.rsplit(".")[-1]
     if user_type is not None:
         filename += user_type.value + '_'
     filename += translation.get_language() + '.' + file_type
     return ('cache', file_type, filename)
Пример #10
0
 def lino_js_parts_chunked(self, actorId):
     """ Like lino_js_parts, but for actor_level data"""
     user_type = get_user_profile()
     filename = 'Lino_' + actorId + "_"
     file_type = self.lino_web_template.rsplit(".")[-1]  # json
     if user_type is not None:
         filename += user_type.value + '_'
     filename += translation.get_language() + '.' + file_type
     return ('cache', file_type, filename)
Пример #11
0
def declare_vars(v):
    """
    Yields the Javascript lines that declare the given  :class:`Variable` `v`.
    If `v` is a :class:`Component`, `list`, `tuple` or `dict` which contains
    other variables, recursively yields also the lines to declare these.
    """
    if isinstance(v, (list, tuple)):
        for sub in v:
            for ln in declare_vars(sub):
                yield ln
        return
    if isinstance(v, dict):
        for sub in v.values():
            for ln in declare_vars(sub):
                yield ln
        return
    if isinstance(v, VisibleComponent) and not v.get_view_permission(
            get_user_profile()):
        return
    if isinstance(v, Component):
        for sub in v.ext_options().values():
            for ln in declare_vars(sub):
                yield ln
        # DON'T return
    elif isinstance(v, Value):
        # 20120616 if not v.is_visible(): return
        # ok = True
        for ln in declare_vars(v.value):
            yield ln
        # DON'T return

    if isinstance(v, Variable):
        # 20120616 if not v.is_visible(): return
        if v.declare_type == DECLARE_VAR:
            yield "var %s = %s;" % (v.ext_name, v.js_value())
        elif v.declare_type == DECLARE_THIS:
            yield "this.%s = %s;" % (v.ext_name, v.js_value())
Пример #12
0
def declare_vars(v):
    """
    Yields the Javascript lines that declare the given  :class:`Variable` `v`.
    If `v` is a :class:`Component`, `list`, `tuple` or `dict` which contains
    other variables, recursively yields also the lines to declare these.
    """
    if isinstance(v, (list, tuple)):
        for sub in v:
            for ln in declare_vars(sub):
                yield ln
        return
    if isinstance(v, dict):
        for sub in list(v.values()):
            for ln in declare_vars(sub):
                yield ln
        return
    if isinstance(v, VisibleComponent) and not v.get_view_permission(
            get_user_profile()):
        return
    if isinstance(v, Component):
        for sub in list(v.ext_options().values()):
            for ln in declare_vars(sub):
                yield ln
        # DON'T return
    elif isinstance(v, Value):
        # 20120616 if not v.is_visible(): return
        # ok = True
        for ln in declare_vars(v.value):
            yield ln
        # DON'T return

    if isinstance(v, Variable):
        # 20120616 if not v.is_visible(): return
        if v.declare_type == DECLARE_VAR:
            yield "var %s = %s;" % (v.ext_name, v.js_value())
        elif v.declare_type == DECLARE_THIS:
            yield "this.%s = %s;" % (v.ext_name, v.js_value())
Пример #13
0
    def write_lino_js(self, f):
        """

        :param f: File object
        :return: 1
        """
        choicelists_data = {
            # ID: [{"value": py2js(c[0]).strip('"'), "text": py2js(c[1]).strip('"')} for c in cl.get_choices()] for
            ID: [{
                "value": c[0].value,
                "text": str(c[1])
            } for c in cl.get_choices()]
            for ID, cl in kernel.CHOICELISTS.items()
        }

        actions = set()
        for rpt in self.actors_list:
            # rh = rpt.get_handle() #for getting bound actor, not needed.
            for ba in rpt.get_actions():
                if ba.action not in actions:
                    actions.add(ba.action)

        self.serialise_js_code = True
        f.write(
            py2js(
                dict(
                    actions={a.action_name: a
                             for a in actions},
                    # actors={a.actor_id: a for a in self.actors_list},
                    menu=settings.SITE.get_site_menu(get_user_profile()),
                    choicelists=choicelists_data,
                    suggestors=list(settings.SITE.plugins.memo.parser.
                                    suggesters.keys())  # [#,@] keytriggers
                ),
                compact=not settings.SITE.is_demo_site))
        self.serialise_js_code = False
        return 1
Пример #14
0
 def is_visible(self):
     if self.hidden:
         return False
     return self.get_view_permission(get_user_profile())
Пример #15
0
def py2js(v, compact=True):
    """Note that None values are rendered as ``null`` (not ``undefined``.

    """
    # assert _for_user_profile is not None
    # logger.debug("py2js(%r)",v)
    for cv in CONVERTERS:
        v = cv(v)

    # if isinstance(v,LanguageInfo):
        # return v.django_code

    if isinstance(v, Value):
        return v.as_ext()
        # v = v.as_ext()
        # if not isinstance(v, basestring):
            # raise Exception("20120121b %r is of type %s" % (v,type(v)))
        # return v
    if isinstance(v, Promise):
        # v = force_text(v)
        return json.dumps(force_text(v.encode('utf8')))

    if isinstance(v, types.GeneratorType):
        return "".join([py2js(x, compact=compact) for x in v])
    if etree.iselement(v):
        return json.dumps(force_text(etree.tostring(v)))
        # try:
        #     return json.dumps(force_text(etree.tostring(v)))
        # except Exception as e:
        #     return json.dumps("Failed to render {!r} : {}".format(v, e))

    # if type(v) is types.GeneratorType:
        # raise Exception("Please don't call the generator function yourself")
        # return "\n".join([ln for ln in v])
    if callable(v):
        # print 20120114, repr(v)
        # raise Exception("Please call the function yourself")
        return "\n".join([ln for ln in v()])
    if isinstance(v, js_code):
        return str(v.s)  # v.s might be a unicode
    if v is None:
        # return 'undefined'
        return 'null'
    if isinstance(v, (list, tuple)):  # (types.ListType, types.TupleType):
        elems = [py2js(x, compact=compact) for x in v
                 if (not isinstance(x, VisibleComponent))
                 or x.get_view_permission(get_user_profile())]
        sep = ", " if compact else ", \n"
        return "[ %s ]" % sep.join(elems)

    if isinstance(v, dict):
        items = [
            # i for i in sorted(v.items(), key=lambda x: str(x))
            # i for i in sorted(v.items())
            i for i in v.items()
            if (not isinstance(v, VisibleComponent))
            or v.get_view_permission(get_user_profile())]

        if six.PY2:
            # "sorted(v.items())" without sortkey caused TypeError when
            # the dictionary contained a mixture of unicode and
            # future.types.newstr objects.
            def sortkey(x):
                if isinstance(x[0], newstr):
                    return six.text_type(x[0])
                return x[0]
        else:
            def sortkey(x):
                return x[0]

        items = sorted(items, key=sortkey)
        # try:
        #     items = sorted(items, key=sortkey)
        # except TypeError as e:
        #     raise TypeError("Failed to sort {0!r} : {1}".format(items, e))
        sep = ", " if compact else ", \n"
        return "{ %s }" % sep.join(
            ["%s: %s" % (py2js(k, compact=compact), py2js(i, compact=compact)) for k, i in items])

    if isinstance(v, bool):  # types.BooleanType:
        return str(v).lower()
    if isinstance(v, Quantity):
        return '"%s"' % v
    if isinstance(v, (int, decimal.Decimal, fractions.Fraction)):
        return str(v)
    if isinstance(v, IncompleteDate):
        return '"%s"' % v.strftime(settings.SITE.date_format_strftime)
    if isinstance(v, datetime.datetime):
        return '"%s"' % v.strftime(settings.SITE.datetime_format_strftime)
    if isinstance(v, datetime.time):
        return '"%s"' % v.strftime(settings.SITE.time_format_strftime)
    if isinstance(v, datetime.date):
        if v.year < 1900:
            v = IncompleteDate.from_date(v)
            return '"%s"' % v.strftime(settings.SITE.date_format_strftime)
        return '"%s"' % v.strftime(settings.SITE.date_format_strftime)

    if isinstance(v, float):
        return repr(v)
    # return json.encoder.encode_basestring(v)
    # print repr(v)
    # http://docs.djangoproject.com/en/dev/topics/serialization/
    # if not isinstance(v, (str,unicode)):
        # raise Exception("20120121 %r is of type %s" % (v,type(v)))
    return json.dumps(v, sort_keys=True,
                  indent=4, separators=(',', ': ')
                      )
Пример #16
0
 def is_visible(self):
     if self.hidden:
         return False
     return self.get_view_permission(get_user_profile())
Пример #17
0
    def py2js_converter(self, v):
        """
        Additional converting logic for serializing Python values to json.
        """
        if v is settings.SITE.LANGUAGE_CHOICES:
            return js_code('LANGUAGE_CHOICES')
        if isinstance(v, choicelists.Choice):
            """
            This is special. We don't render the text but the value.
            """
            return v.value
        if isinstance(v, models.Model):
            return v.pk
        if isinstance(v, Exception):
            return str(v)
        if isinstance(v, Menu):
            if v.parent is None:
                return v.items
                # kw.update(region='north',height=27,items=v.items)
                # return py2js(kw)
            return dict(text=v.label, menu=dict(items=v.items))

        if isinstance(v, MenuItem):
            if v.instance is not None:
                h = self.instance_handler(None, v.instance, None)
                assert h is not None
                js = "%s" % h
                return self.handler_item(v, h, None)
            elif v.bound_action is not None:
                if v.params:
                    ar = v.bound_action.request(**v.params)
                    js = self.request_handler(ar)
                else:
                    js = self.action_call(None, v.bound_action, {})
                return self.handler_item(v, js, v.help_text)

            elif v.javascript is not None:
                js = "%s" % v.javascript
                return self.handler_item(v, js, v.help_text)
            elif v.href is not None:
                url = v.href
            # ~ elif v.request is not None:
            # ~ raise Exception("20120918 request %r still used?" % v.request)
            # ~ url = self.get_request_url(v.request)
            else:
                # a separator
                # ~ return dict(text=v.label)
                return v.label
                # ~ url = self.build_url('api',v.action.actor.app_label,v.action.actor.__name__,fmt=v.action.name)
            if v.parent.parent is None:
                # special case for href items in main menubar
                return dict(
                    xtype='button',
                    text=v.label,
                    # ~ handler=js_code("function() { window.location='%s'; }" % url))
                    handler=js_code("function() { Lino.load_url('%s'); }" %
                                    url))
            return dict(text=v.label, href=url)
        if issubclass(v.__class__, LayoutElement):
            # Layout elems
            result = dict(label=v.get_label(),
                          repr=repr(v),
                          react_name=v.__class__.__name__
                          )  # Used for choosing correct react component
            if hasattr(v, "elements"):  # dd
                result['items'] = [
                    e for e in v.elements
                    if e.get_view_permission(get_user_profile())
                ]
            result.update(
                obj2dict(
                    v,
                    "fields_index fields_index_hidden editable vertical hpad is_fieldset name width preferred_width\
                                      hidden value hflex vflex"))
            # result["width"] = v.width or v.preferred_width
            # Slave tables
            if hasattr(v, "actor"):
                # reference to actor data for slave-grids
                result.update(obj2dict(
                    v.actor, "actor_id"))  # to get siteDate layout index

            # if hasattr(v, "get_field_options"):
            if issubclass(v.__class__, FieldElement):
                result.update(field_options=v.get_field_options())
                result.update(help_text=v.field.help_text)

            return result
        if isinstance(v, LayoutHandle):
            # Layout entry-point
            return dict(main=v.main, window_size=v.layout.window_size)

        if isinstance(v, Action):
            # todo include all aux info
            # todo include grid info
            # todo refactor this into a all_actions object and have the bound actions ref it to reduse json size
            result = dict(
                an=v.action_name,
                label=v.get_label(
                ),  # todo fix this, this is a readable action, not ID for the action
                window_action=v.is_window_action(),
                http_method=v.http_method,
            )

            # if v.show_in_bbar: result["bbar"] = True # not needed
            if v.preprocessor: result["preprocessor"] = v.preprocessor
            if v.combo_group: result["combo_group"] = v.combo_group
            if v.select_rows: result['select_rows'] = v.select_rows
            if v.submit_form_data: result['submit_form_data'] = True
            if v.button_text: result['button_text'] = v.button_text

            icon = self.get_action_icon(v)
            if icon: result['icon'] = icon

            return result

        if isinstance(v, BoundAction):

            # todo include all aux info
            # todo include grid info
            # todo refactor this into a all_actions object and have the bound actions ref it to reduse json size
            result = dict(
                an=v.action.action_name,
                window_layout=v.get_layout_handel(),
            )

            # if v.action.show_in_bbar: result["bbar"] = True # not needed
            if v.action.window_type:
                tba = []
                combo_group = None
                for ba in v.actor.get_toolbar_actions(v.action):
                    if ba.action.combo_group == combo_group and combo_group is not None:
                        previous = tba.pop()
                        if not isinstance(previous, list):
                            previous = [previous]
                        previous.append(ba.action.action_name)
                        tba.append(previous)
                    else:  # is normal
                        tba.append(ba.action.action_name)
                        combo_group = ba.action.combo_group
                result["toolbarActions"] = tba

            return result
        if isclass(v) and issubclass(v, Actor):
            result = dict(
                id=v.actor_id,
                ba=v._actions_dict,
                label=v.get_actor_label(),
                slave=bool(v.master),
                editable=v.editable,
                # [py2js(b) for b in v.actions.items()]
            )
            # grids
            if hasattr(v.get_handle(), "get_columns"):
                result['col'] = v.get_handle().get_columns()
                index_mod = 0
                for c in result['col']:
                    c.fields_index = find(v.get_handle().store.list_fields,
                                          c.field.name,
                                          key=lambda f: f.name) + index_mod
                    if isinstance(c, ComboFieldElement) and not isinstance(
                            c, SimpleRemoteComboFieldElement):
                        # Skip the data value for multi value columns, such as choices and FK fields.
                        # use c.fields_index -1 for data value
                        c.fields_index_hidden = c.fields_index + 1
                        index_mod += 1

            result.update(obj2dict(v.get_handle().store,
                                   "pk_index"))  # Data index which is the PK
            result.update(
                obj2dict(
                    v,
                    "preview_limit "  # number of rows to render # if 0 no paginator.
                    "use_detail_param_panel "  # show PV panel in detail
                    "use_detail_params_value "  # in grid, use parrent PV values
                    "hide_top_toolbar "  # No selection and toolbar
                    "use_detail_params_value "
                    "react_responsive "
                    "react_big_search "
                    "display_mode "
                    "max_render_depth "
                    "simple_slavegrid_header "
                    "paginator_template "
                    "hide_if_empty "
                    "borderless_list_mode "))

            card_layout = getattr(v, "card_layout", None)
            if card_layout is not None:
                result.update(card_layout=card_layout.get_layout_handle())
            # mt + slave-tables

            chooser_dict = getattr(v.model, "_choosers_dict", {})
            if chooser_dict:
                result.update(
                    chooser_dict={
                        fn: [cf.name for cf in c.context_fields]
                        for fn, c in chooser_dict.items()
                    })

            if settings.SITE.is_installed('contenttypes') and getattr(
                    v, 'model', None) is not None and hasattr(
                        v.model, "_meta"):
                # Perhaps I should have the model also be py2js'd?
                result.update(
                    content_type=ContentType.objects.get_for_model(v.model).pk)
            for a in "detail_action insert_action default_action".split(" "):
                if hasattr(v, a) and getattr(v, a) is not None:
                    result.update({a: getattr(v, a).action.action_name})

            if v.params_layout is not None:
                # Param field array and layout struct
                result.update(
                    pv_layout=v.params_layout.get_layout_handle(),
                    pv_fields=[
                        f.name
                        for f in v.params_layout.params_store.param_fields
                    ],
                )
            return result

        if isinstance(v, js_code) and getattr(self, "serialise_js_code",
                                              False):
            # Convert js_code into strings so they are serialised. rather than displayed w/o quotes
            return str(v.s)

        return v
Пример #18
0
    def js_render_FormPanelSubclass(self, dh):

        tbl = dh.layout._datasource
        if not dh.main.get_view_permission(get_user_profile()):
            msg = "No view permission for main panel of %s :" % \
                  dh.layout._formpanel_name
            msg += " main requires %s (actor %s requires %s)" % (
                dh.main.required_roles, tbl, tbl.required_roles)
            # ~ raise Exception(msg)
            logger.warning(msg)
            print(20150717, msg)
            return

        yield ""
        # yield "Lino.%s = Ext.extend(Lino.FormPanel,{" % \
        #     dh.layout._formpanel_name
        yield "Ext.define('Lino.%s', { extend : 'Lino.FormPanel'," % \
              dh.layout._formpanel_name
        yield "  layout: 'fit',"
        yield "  auto_save: true,"
        if dh.layout.window_size and dh.layout.window_size[1] == 'auto':
            yield "  autoHeight: true,"
        if settings.SITE.is_installed('contenttypes') and issubclass(
                tbl, dbtables.Table):
            yield "  content_type: %s," % py2js(
                ContentType.objects.get_for_model(tbl.model).pk)
        if not tbl.editable:
            yield "  disable_editing: true,"
        if not tbl.auto_apply_params:
            yield "  auto_apply_params: false,"
        if dh.layout._formpanel_name.endswith('.InsertFormPanel'):
            yield "  default_record_id: -99999,"

        yield "  initComponent : function() {"
        # 20140503 yield "    var containing_panel = this;"
        # yield "// user user_type: %s" % jsgen._for_user_profile
        lc = 0
        for ln in jsgen.declare_vars(dh.main):
            yield "    " + ln
            lc += 1
        if lc == 0:
            raise Exception("%r of %s has no variables" % (dh.main, dh))
        yield "    this.items = %s;" % dh.main.as_ext()
        # ~ if issubclass(tbl,tables.AbstractTable):
        if True:
            yield "    this.before_row_edit = function(record) {"
            for ln in self.before_row_edit(dh.main):
                yield "      " + ln
            yield "    }"
        on_render = self.build_on_render(dh.main)
        if on_render:
            yield "    this.onRender = function(ct, position) {"
            for ln in on_render:
                yield "      " + ln
            yield "      Lino.%s.superclass.onRender.call(this, ct, position);" % \
                  dh.layout._formpanel_name
            # yield "      this.callSuper(ct, position);"
            yield "    }"

        # yield "    Lino.%s.superclass.initComponent.call(this);" % \
        #     dh.layout._formpanel_name
        yield "  this.callSuper();"

        # Add a change listener to active fields. This which will
        # cause automatic form submit when some actiove field is
        # changed.
        if dh.layout._formpanel_name.endswith('.DetailFormPanel'):
            if tbl.active_fields:
                yield '    // active_fields:'
                for name in tbl.active_fields:
                    e = dh.main.find_by_name(name)
                    if e is not None:  # 20120715
                        if True:  # see actions.ValidateForm
                            f = 'function(){ this.save() }'
                        else:
                            f = 'function(){ this.validate_form() }'
                        yield '    %s.on("change", %s, this);' % (py2js(e), f)
        yield "  }"
        yield "});"
        yield ""
Пример #19
0
def py2js(v):
    """Note that None values are rendered as ``null`` (not ``undefined``.

    """
    # assert _for_user_profile is not None
    # logger.debug("py2js(%r)",v)
    for cv in CONVERTERS:
        v = cv(v)

    # if isinstance(v,LanguageInfo):
        # return v.django_code

    if isinstance(v, Value):
        return v.as_ext()
        # v = v.as_ext()
        # if not isinstance(v, basestring):
            # raise Exception("20120121b %r is of type %s" % (v,type(v)))
        # return v
    if isinstance(v, Promise):
        # v = force_text(v)
        return json.dumps(force_text(v.encode('utf8')))

    if isinstance(v, types.GeneratorType):
        return "".join([py2js(x) for x in v])
    if etree.iselement(v):
        return json.dumps(force_text(etree.tostring(v)))
        # try:
        #     return json.dumps(force_text(etree.tostring(v)))
        # except Exception as e:
        #     return json.dumps("Failed to render {!r} : {}".format(v, e))

    # if type(v) is types.GeneratorType:
        # raise Exception("Please don't call the generator function yourself")
        # return "\n".join([ln for ln in v])
    if callable(v):
        # print 20120114, repr(v)
        # raise Exception("Please call the function yourself")
        return "\n".join([ln for ln in v()])
    if isinstance(v, js_code):
        return str(v.s)  # v.s might be a unicode
    if v is None:
        # return 'undefined'
        return 'null'
    if isinstance(v, (list, tuple)):  # (types.ListType, types.TupleType):
        elems = [py2js(x) for x in v
                 if (not isinstance(x, VisibleComponent))
                 or x.get_view_permission(get_user_profile())]
        return "[ %s ]" % ", ".join(elems)

    if isinstance(v, dict):
        items = [
            # i for i in sorted(v.items(), key=lambda x: str(x))
            # i for i in sorted(v.items())
            i for i in v.items()
            if (not isinstance(v, VisibleComponent))
            or v.get_view_permission(get_user_profile())]

        if six.PY2:
            # "sorted(v.items())" without sortkey caused TypeError when
            # the dictionary contained a mixture of unicode and
            # future.types.newstr objects.
            def sortkey(x):
                if isinstance(x[0], newstr):
                    return six.text_type(x[0])
                return x[0]
        else:
            def sortkey(x):
                return x[0]
            
        items = sorted(items, key=sortkey)
        # try:
        #     items = sorted(items, key=sortkey)
        # except TypeError as e:
        #     raise TypeError("Failed to sort {0!r} : {1}".format(items, e))
        return "{ %s }" % ", ".join(
            ["%s: %s" % (py2js(k), py2js(i)) for k, i in items])

    if isinstance(v, bool):  # types.BooleanType:
        return str(v).lower()
    if isinstance(v, Quantity):
        return '"%s"' % v
    if isinstance(v, (int, decimal.Decimal, fractions.Fraction)):
        return str(v)
    if isinstance(v, IncompleteDate):
        return '"%s"' % v.strftime(settings.SITE.date_format_strftime)
    if isinstance(v, datetime.datetime):
        return '"%s"' % v.strftime(settings.SITE.datetime_format_strftime)
    if isinstance(v, datetime.time):
        return '"%s"' % v.strftime(settings.SITE.time_format_strftime)
    if isinstance(v, datetime.date):
        if v.year < 1900:
            v = IncompleteDate.from_date(v)
            return '"%s"' % v.strftime(settings.SITE.date_format_strftime)
        return '"%s"' % v.strftime(settings.SITE.date_format_strftime)

    if isinstance(v, float):
        return repr(v)
    # return json.encoder.encode_basestring(v)
    # print repr(v)
    # http://docs.djangoproject.com/en/dev/topics/serialization/
    # if not isinstance(v, (str,unicode)):
        # raise Exception("20120121 %r is of type %s" % (v,type(v)))
    return json.dumps(v)