Example #1
0
    def get(self, request, *args, **kwargs):
        try:
            project_id = request.GET["project_id"]
        except (KeyError, MultiValueDictKeyError):
            project_id = None

        try:
            calls = json.loads(request.GET["calls"])
        except ValueError:
            return HttpResponseBadRequest()

        results = []
        for call in calls:
            obj = get_object_by_dotted_name(call["id"], project_id=project_id)
            view_klass = globals()[obj.__class__.__name__ + "Detail"]
            view = view_klass().as_view()(request=request,
                                          object=obj,
                                          data=call["data"],
                                          format="json")
            if view.status_code == 500:
                return HttpResponseServerError(view.content)
            rendered = view.render().content
            results.append(json.loads(rendered))

        return JsonResponse({"results": results})
Example #2
0
    def _get_children(self, klass, subdirectory=""):
        """Helper method to fetch children of a certain type"""
        li = []

        # Our own
        t = select_template([self.project.relative_path + "metadata.json"])
        pth = os.path.join(os.path.dirname(t.template.origin.name), "..",
                           self.relative_path, subdirectory)
        if os.path.exists(pth):
            li = [
                klass(id, self) for id in os.listdir(pth)
                if not id.startswith(".") and not id in RESERVED_IDS
            ]

        # Ask parents
        attr = klass.__name__.lower() + "s"
        for project_id in reversed(self.project.metadata.get("parents", [])):
            obj = get_object_by_dotted_name(
                ".".join([str(project_id)] + self.dotted_name.split(".")[1:]))
            li.extend(getattr(obj, attr))

        # Remove duplicates
        processed = []
        result = []
        for l in li:
            if l.id not in processed:
                result.append(l)
                processed.append(l.id)

        return sorted(result, key=lambda item: item.metadata.get("position"))
Example #3
0
    def get(self, request, *args, **kwargs):
        try:
            calls = json.loads(request.GET["calls"])
        except ValueError:
            return HttpResponseBadRequest()

        results = []
        for call in calls:
            obj = get_object_by_dotted_name(call["id"])
            view_klass = globals()[obj.__class__.__name__ + "Detail"]
            view = view_klass().as_view()(request=request,
                                          object=obj,
                                          data=call["data"],
                                          format="json")
            rendered = view.render().content
            results.append(json.loads(rendered))

        return JsonResponse({"results": results})
Example #4
0
 def test_metadata_panel(self):
     """We inherit panel fully"""
     obj = get_object_by_dotted_name("myprojectchild.website.atoms.panel")
     self.assertEqual(obj.metadata["title"], "Panel")
Example #5
0
 def test_metadata_anchor(self):
     """We override anchor element but not data"""
     obj = get_object_by_dotted_name("myprojectchild.website.atoms.anchor")
     self.assertEqual(obj.metadata["title"], "Anchor")
Example #6
0
 def test_metadata_label(self):
     """We override label element and data"""
     obj = get_object_by_dotted_name("myprojectchild.website.atoms.label")
     self.assertEqual(obj.metadata["title"], "MyProjectChild label")
Example #7
0
    def render(self, context):
        # We must import late
        from mote.models import Project, Aspect, Pattern, Element, Variation

        element_or_identifier = self.element_or_identifier.resolve(context)

        # If element_or_identifier is a string convert it
        if isinstance(element_or_identifier, string_types):
            # The "self" project triggers a project lookup. It first checks for
            # a context variable (used internally by the Mote explorer) then
            # for a setting (used when calling render_element over the API).
            if element_or_identifier.startswith("self."):
                project_id = context.get("__mote_project_id__", None)
                if project_id is None:
                    try:
                        value = settings.MOTE["project"]
                    except (AttributeError, KeyError):
                        raise RuntimeError(
                            "Define MOTE[\"project\"] setting for project lookup"
                        )
                    if callable(value):
                        project_id = value(context["request"])
                    else:
                        project_id = value
                obj = get_object_by_dotted_name(
                    element_or_identifier.replace("self.", project_id + "."))
            else:
                obj = get_object_by_dotted_name(element_or_identifier)
            if not isinstance(obj, (Element, Variation)):
                raise template.TemplateSyntaxError("Invalid identifier %s" %
                                                   element_or_identifier)
        else:
            obj = element_or_identifier

        # Set the object in the context as "element"
        with context.push():
            context["element"] = obj

            # Resolve the kwargs
            resolved = {}
            for k, v in self.kwargs.items():
                try:
                    r = v.resolve(context)
                except VariableDoesNotExist:
                    continue
                if isinstance(r, Promise):
                    r = text_type(r)

                # Strings may be interpreted further
                if isinstance(r, string_types):

                    # Attempt to resolve any variables by rendering
                    t = template.Template(r)
                    raw_struct = t.render(context)

                    # Attempt to convert to JSON
                    try:
                        resolved[k] = json.loads(raw_struct)
                    except ValueError:
                        resolved[k] = r

                else:
                    resolved[k] = r

            # Find the correct view and construct view kwargs
            view_kwargs = dict(
                project=obj.project.id,
                aspect=obj.aspect.id,
                pattern=obj.pattern.id,
            )
            if isinstance(obj, Variation):
                view = VariationPartialView
                view_kwargs.update(
                    dict(element=obj.element.id, variation=obj.id))
            else:
                view = ElementPartialView
                view_kwargs.update(dict(element=obj.id))

# Compute a cache key before we pop from resolved
            li = [obj.modified, deephash(resolved)]
            li.extend(frozenset(sorted(view_kwargs.items())))
            hashed = md5(u":".join([text_type(l)
                                    for l in li]).encode("utf-8")).hexdigest()
            cache_key = "render-element-%s" % hashed
            cached = cache.get(cache_key, None)
            if cached is not None:
                return cached

        # Automatically perform masking with the default data
            request = context["request"]
            masked = obj.data
            # Omit top-level key
            if masked:
                masked = masked[list(masked.keys())[0]]
            if "data" in request.GET:
                masked = deepmerge(masked, json.loads(request.GET["data"]))
            elif "data" in resolved:
                masked = deepmerge(masked, resolved.pop("data"))
            context["data"] = masked

            # Construct a final kwargs that includes the context
            final_kwargs = context.flatten()
            del final_kwargs["request"]
            final_kwargs.update(resolved)
            final_kwargs.update(view_kwargs)

            # Call the view. Let any error propagate.
            result = view.as_view()(request, **final_kwargs)

            if isinstance(result, TemplateResponse):
                # The result of a generic view
                result.render()
                html = result.rendered_content
            elif isinstance(result, HttpResponse):
                # Old-school view
                html = result.content

            # Make output beautiful for Chris
            if not settings.DEBUG:
                beauty = BeautifulSoup(html, "html.parser")
                html = beauty.prettify()

            cache.set(cache_key, html, 300)
            return html
Example #8
0
    def render(self, context):
        # We must import late
        from mote.models import Project, Aspect, Pattern, Element, Variation

        # To keep templates as simple as possible we don't require quotes to
        # denote a string. That requires special handling.
        try:
            element_or_identifier = self.element_or_identifier.resolve(context)
        except template.VariableDoesNotExist:
            element_or_identifier = \
                context["element"].data[self.element_or_identifier.var]

        data = OrderedDict()
        if self.data:
            data = self.data.resolve(context)

        # Shortcut notation allows an element to be looked up from data
        if isinstance(element_or_identifier, dict):
            copied = deepcopy(element_or_identifier)
            element_or_identifier = copied.pop("id")
            data = copied

        # If element_or_identifier is a string convert it
        if isinstance(element_or_identifier, string_types):

            # The "self" project triggers a project lookup. It first checks for
            # a context variable (used internally by the Mote explorer) then
            # for a setting (used when calling render over the API).
            if element_or_identifier.startswith("self."):
                project_id = context.get("__mote_project_id__", None)
                if project_id is None:
                    try:
                        value = settings.MOTE["project"]
                    except (AttributeError, KeyError):
                        raise RuntimeError(
                            "Define MOTE[\"project\"] setting for project lookup"
                        )
                    if callable(value):
                        project_id = value(context["request"])
                    else:
                        project_id = value
                obj = get_object_by_dotted_name(
                    element_or_identifier.replace("self.", project_id + "."))

            # Non-self lookup
            else:
                obj = get_object_by_dotted_name(element_or_identifier)

            # Type check
            if not isinstance(obj, (Element, Variation)):
                raise template.TemplateSyntaxError("Invalid identifier %s" %
                                                   element_or_identifier)

        elif isinstance(element_or_identifier, (Element, Variation)):
            obj = element_or_identifier
            data = context.get("data")

        else:
            raise RuntimeError("Cannot identify %r" % element_or_identifier)

        with context.push():

            # We use a completely clean context to avoid leakage
            newcontext = {}

            # Set the object in the new context as "element"
            newcontext["element"] = obj

            # Convert self.data if possible
            if isinstance(data, Promise):
                data = text_type(data)

            # Strings may be interpreted further
            if isinstance(data, string_types):

                # Attempt to resolve any variables by rendering
                t = template.Template(data)
                raw_struct = t.render(context)

                # Attempt to convert to JSON
                try:
                    data = json.loads(raw_struct)
                except ValueError:
                    pass

            # Find the correct view and construct view kwargs
            view_kwargs = dict(
                project=obj.project.id,
                aspect=obj.aspect.id,
                pattern=obj.pattern.id,
            )
            if isinstance(obj, Variation):
                view = VariationPartialView
                view_kwargs.update(
                    dict(element=obj.element.id, variation=obj.id))
            else:
                view = ElementPartialView
                view_kwargs.update(dict(element=obj.id))

            # Compute a cache key
            li = [obj.checksum, deephash(data)]
            li.extend(frozenset(sorted(view_kwargs.items())))
            hashed = md5(":".join([text_type(l)
                                   for l in li]).encode("utf-8")).hexdigest()
            cache_key = "render-element-%s" % hashed
            cached = cache.get(cache_key, None)
            if cached is not None:
                return cached

        # Automatically perform masking with the default data
            request = context["request"]
            masked = obj.data
            if "data" in request.GET:
                masked = deepmerge(masked, json.loads(request.GET["data"]))
            elif data:
                masked = deepmerge(masked, data)

            # Set data on new context. Also set the keys in data directly on
            # new context to make for cleaner templates.
            newcontext["data"] = masked
            for k, v in masked.items():
                if k in ("data", "element", "original_element", "pretty_data"):
                    raise RuntimeError("%s is a reserved key" % k)
                newcontext[k] = v

            # Update new context with the view kwargs
            newcontext.update(view_kwargs)

            # Make data legible in debug mode
            if settings.DEBUG:
                newcontext["pretty_data"] = json.dumps(newcontext["data"],
                                                       indent=4)

            # Call the view. Let any error propagate.
            result = view.as_view()(request, **newcontext)

            if isinstance(result, TemplateResponse):
                # The result of a generic view
                result.render()
                html = result.rendered_content
            elif isinstance(result, HttpResponse):
                # Old-school view
                html = result.content

            # Make output beautiful for Chris. This is expensive but required
            # for production. Make it togglable for develop.
            if not settings.DEBUG or request.GET.get("beautify", False):
                beauty = BeautifulSoup(html, "html.parser")
                html = beauty.prettify()

            # Useful debug info
            if settings.DEBUG:
                html = "<!-- " + obj.dotted_name + " -->" + html

            cache.set(cache_key, html, 300)
            return html