Example #1
0
def test_draft_reversion():
    view_name = printable_gibberish()
    theme = TestTheme()
    placeholder_name = "test_ph"
    vc = ViewConfig(theme=theme, view_name=view_name, draft=True)

    def get_layout_data(draft):
        # shorthand -- we're going to be doing this a lot in this test case
        return ViewConfig(
            theme=theme, view_name=view_name,
            draft=draft).saved_view_config.get_layout_data(placeholder_name)

    data1 = {printable_gibberish(): True}
    data2 = {printable_gibberish(): True}
    vc.save_placeholder_layout(placeholder_name, data1)
    vc.publish()

    assert get_layout_data(draft=False) == data1
    assert get_layout_data(draft=True) == data1
    vc = ViewConfig(theme=theme, view_name=view_name, draft=True)
    svc = vc.saved_view_config
    assert svc.draft
    assert svc.get_layout_data(placeholder_name) == data1
    # Make changes over the last published version
    svc.set_layout_data(placeholder_name, data2)
    svc.save()
    # Still all good in public?
    assert get_layout_data(draft=False) == data1
    # Still got it in draft?
    assert get_layout_data(draft=True) == data2
    # Actually revert those draft changes now!
    vc.revert()
    # So in draft we're back to the published version, right?
    assert get_layout_data(draft=True) == data1
Example #2
0
def test_unthemebound_view_config_cant_do_much():
    vc = ViewConfig(theme=None, view_name="durr", draft=True)
    with pytest.raises(ValueError):
        vc.publish()
    with pytest.raises(ValueError):
        vc.revert()
    with pytest.raises(ValueError):
        vc.save_placeholder_layout("hurr", {"foo": True})
    l = vc.get_placeholder_layout("hurr")  # loading should work, but . . .
    assert not len(l.rows)  # . . . there shouldn't be much in there
Example #3
0
def test_draft_reversion():
    view_name = printable_gibberish()
    theme = TestTheme()
    placeholder_name = "test_ph"
    vc = ViewConfig(theme=theme, view_name=view_name, draft=True)
    def get_layout_data(draft):
        # shorthand -- we're going to be doing this a lot in this test case
        return ViewConfig(theme=theme, view_name=view_name, draft=draft).saved_view_config.get_layout_data(placeholder_name)
    data1 = {printable_gibberish(): True}
    data2 = {printable_gibberish(): True}
    vc.save_placeholder_layout(placeholder_name, data1)
    vc.publish()

    assert get_layout_data(draft=False) == data1
    assert get_layout_data(draft=True) == data1
    vc = ViewConfig(theme=theme, view_name=view_name, draft=True)
    svc = vc.saved_view_config
    assert svc.draft
    assert svc.get_layout_data(placeholder_name) == data1
    # Make changes over the last published version
    svc.set_layout_data(placeholder_name, data2)
    svc.save()
    # Still all good in public?
    assert get_layout_data(draft=False) == data1
    # Still got it in draft?
    assert get_layout_data(draft=True) == data2
    # Actually revert those draft changes now!
    vc.revert()
    # So in draft we're back to the published version, right?
    assert get_layout_data(draft=True) == data1
Example #4
0
def get_view_config(context, global_type=False):
    """
    Get a view configuration object for a Jinja2 rendering context.

    :param context: Rendering context
    :type context: jinja2.runtime.Context
    :param global_type: Boolean indicating whether this is a global type
    :type global_type: bool|False
    :return: View config
    :rtype: shoop.xtheme.view_config.ViewConfig
    """
    # This uses the Jinja context's technically-immutable vars dict
    # to cache the view configuration. This is fine in our case, I'd say.
    request = context.get("request")
    config_key = "_xtheme_global_view_config" if global_type else "_xtheme_view_config"
    config = context.vars.get(config_key)
    if (config is None):
        view_object = context.get("view")
        if view_object:
            view_class = view_object.__class__
            view_name = view_class.__name__
        else:
            view_name = "UnknownView"
        config = ViewConfig(
            theme=get_current_theme(request),
            view_name=view_name,
            draft=is_edit_mode(request),
            global_type=global_type,
        )
        context.vars[config_key] = config
    return config
Example #5
0
def get_view_config(context):
    """
    Get a view configuration object for a Jinja2 rendering context.

    :param context: Rendering context
    :type context: jinja2.runtime.Context
    :return: View config
    :rtype: shoop.xtheme.view_config.ViewConfig
    """
    # This uses the Jinja context's technically-immutable vars dict
    # to cache the view configuration. This is fine in our case, I'd say.
    request = context.get("request")
    config = context.vars.get("_xtheme_view_config")
    if config is None:
        view_object = context.get("view")
        if view_object:
            view_class = view_object.__class__
            view_name = view_class.__name__
        else:
            view_name = "UnknownView"
        config = ViewConfig(
            theme=get_current_theme(request),
            view_name=view_name,
            draft=is_edit_mode(request)
        )
        context.vars["_xtheme_view_config"] = config
    return config
Example #6
0
def test_load_save_default():
    view_name = printable_gibberish()
    theme = TestTheme()
    vc = ViewConfig(theme=theme, view_name=view_name, draft=True)
    placeholder_name = "test_ph"
    data = {"dummy": True}
    assert not vc.saved_view_config.get_layout_data(placeholder_name)
    assert vc.save_default_placeholder_layout(placeholder_name, data)
    assert not vc.save_default_placeholder_layout(placeholder_name, data)

    # Not in public mode yet, right?
    assert not ViewConfig(theme=theme, view_name=view_name, draft=False).saved_view_config.get_layout_data(placeholder_name)

    # But it is in drafts, even if we reload it?
    vc = ViewConfig(theme=theme, view_name=view_name, draft=True)
    assert vc.saved_view_config.get_layout_data(placeholder_name) == data
Example #7
0
def test_unthemebound_view_config_cant_do_much():
    vc = ViewConfig(theme=None, view_name="durr", draft=True)
    with pytest.raises(ValueError):
        vc.publish()
    with pytest.raises(ValueError):
        vc.revert()
    with pytest.raises(ValueError):
        vc.save_placeholder_layout("hurr", {"foo": True})
    l = vc.get_placeholder_layout("hurr")  # loading should work, but . . .
    assert not len(l.rows)  # . . . there shouldn't be much in there
Example #8
0
 def _populate_vars(self):
     theme = get_theme_by_identifier(self.request.GET["theme"])
     if not theme:
         raise Problem(_("Unable to determine current theme."))
     self.view_config = ViewConfig(theme=theme,
                                   view_name=self.request.GET["view"],
                                   draft=True)
     self.placeholder_name = self.request.GET["ph"]
     self.default_layout = self._get_default_layout()
     self.layout = self.view_config.get_placeholder_layout(
         placeholder_name=self.placeholder_name,
         default_layout=self.default_layout)
     (x, y) = self.current_cell_coords = (
         int(self.request.GET.get("x", -1)),
         int(self.request.GET.get("y", -1)),
     )
     self.current_cell = self.layout.get_cell(x=x, y=y)
     self.build_form()
Example #9
0
def test_load_save_default():
    view_name = printable_gibberish()
    theme = ATestTheme()
    vc = ViewConfig(theme=theme, view_name=view_name, draft=True)
    placeholder_name = "test_ph"
    data = {"dummy": True}
    assert not vc.saved_view_config.get_layout_data(placeholder_name)
    assert vc.save_default_placeholder_layout(placeholder_name, data)
    assert not vc.save_default_placeholder_layout(placeholder_name, data)

    # Not in public mode yet, right?
    assert not ViewConfig(theme=theme, view_name=view_name, draft=False).saved_view_config.get_layout_data(placeholder_name)

    # But it is in drafts, even if we reload it?
    vc = ViewConfig(theme=theme, view_name=view_name, draft=True)
    assert vc.saved_view_config.get_layout_data(placeholder_name) == data
Example #10
0
 def _populate_vars(self):
     theme = get_theme_by_identifier(self.request.GET["theme"])
     if not theme:
         raise Problem("Unable to determine current theme.")
     self.view_config = ViewConfig(
         theme=theme,
         view_name=self.request.GET["view"],
         draft=True
     )
     self.placeholder_name = self.request.GET["ph"]
     self.default_layout = self._get_default_layout()
     self.layout = self.view_config.get_placeholder_layout(
         placeholder_name=self.placeholder_name,
         default_layout=self.default_layout
     )
     (x, y) = self.current_cell_coords = (
         int(self.request.GET.get("x", -1)),
         int(self.request.GET.get("y", -1)),
     )
     self.current_cell = self.layout.get_cell(x=x, y=y)
     self.build_form()
Example #11
0
def test_unsaved_vc_reversion():
    vc = ViewConfig(theme=TestTheme(),
                    view_name=printable_gibberish(),
                    draft=True)
    vc.revert(
    )  # No-op, since this has never been saved (but shouldn't crash either)
Example #12
0
class EditorView(TemplateView):
    template_name = "shoop/xtheme/editor.jinja"
    xtheme_injection = False  # We don't need the editing injection here, so opt-out
    changed = False  # Overridden in `save_layout`

    def _get_default_layout(self):
        try:
            return json.loads(self.request.GET["default_config"])
        except (ValueError, KeyError):
            return None

    def get_context_data(self, **kwargs):  # doccov: ignore
        ctx = super(EditorView, self).get_context_data(**kwargs)
        ctx["layout"] = self.layout
        ctx["csrf_token_str"] = get_token(self.request)
        # ctx["layout_debug"] = pformat(ctx["layout"].serialize())
        ctx["current_cell_coords"] = self.current_cell_coords
        ctx["current_cell"] = self.current_cell
        ctx["form"] = self.form
        ctx["changed"] = self.changed
        ctx["cell_limit"] = ROW_CELL_LIMIT
        return ctx

    def dispatch(self, request, *args, **kwargs):  # doccov: ignore
        if not could_edit(request):
            raise Problem("No access to editing")
        self._populate_vars()
        if self.default_layout:
            self.view_config.save_default_placeholder_layout(self.placeholder_name, self.default_layout)
            # We saved the default layout, so get rid of the humongous GET arg and try again
            get_args = dict(self.request.GET.items())
            get_args.pop("default_config", None)
            return HttpResponseRedirect("%s?%s" % (self.request.path, urlencode(get_args)))
        return super(EditorView, self).dispatch(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):  # doccov: ignore
        command = request.POST.get("command")
        if command:
            dispatcher = getattr(self, "dispatch_%s" % command, None)
            if not callable(dispatcher):
                raise Problem("Unknown command %s" % command)
            dispatch_kwargs = dict(request.POST.items())
            rv = dispatcher(**dispatch_kwargs)
            if rv:
                return rv
            self.request.method = "GET"  # At this point, we won't want to cause form validation
            self.build_form()  # and it's not a bad idea to rebuild the form
            return super(EditorView, self).get(request, *args, **kwargs)

        if request.POST.get("save") and self.form and self.form.is_valid():
            self.form.save()
            self.save_layout()

        return super(EditorView, self).get(request, *args, **kwargs)

    def _populate_vars(self):
        theme = get_theme_by_identifier(self.request.GET["theme"])
        if not theme:
            raise Problem("Unable to determine current theme.")
        self.view_config = ViewConfig(
            theme=theme,
            view_name=self.request.GET["view"],
            draft=True
        )
        self.placeholder_name = self.request.GET["ph"]
        self.default_layout = self._get_default_layout()
        self.layout = self.view_config.get_placeholder_layout(
            placeholder_name=self.placeholder_name,
            default_layout=self.default_layout
        )
        (x, y) = self.current_cell_coords = (
            int(self.request.GET.get("x", -1)),
            int(self.request.GET.get("y", -1)),
        )
        self.current_cell = self.layout.get_cell(x=x, y=y)
        self.build_form()

    def build_form(self):
        if not self.current_cell:
            self.form = None
            return
        kwargs = {
            "layout_cell": self.current_cell
        }
        if self.request.method == "POST":
            kwargs["data"] = self.request.POST
            kwargs["files"] = self.request.FILES
        self.form = LayoutCellFormGroup(**kwargs)

    def save_layout(self, layout=None):
        self.view_config.save_placeholder_layout(
            placeholder_name=self.placeholder_name,
            layout=(layout or self.layout)
        )
        self.changed = True

    def dispatch_add_cell(self, y, **kwargs):
        y = int(y)
        if len(self.layout.rows[y].cells) >= ROW_CELL_LIMIT:
            raise ValueError(_("Cannot add more than %d cells in one row.") % ROW_CELL_LIMIT)

        if not (0 <= y < len(self.layout.rows)):
            # No need to raise an exception, really.
            # It must have been a honest mistake.
            return
        self.layout.rows[y].add_cell()
        self.save_layout()

    def dispatch_add_row(self, y=None, **kwargs):
        row = self.layout.insert_row(y)
        row.add_cell()  # For convenience, add a cell to the row.
        self.save_layout()

    def dispatch_del_row(self, y, **kwargs):
        self.layout.delete_row(y)
        self.save_layout()

    def dispatch_del_cell(self, x, y, **kwargs):
        self.layout.delete_cell(x, y)
        self.save_layout()

    def dispatch_change_plugin(self, plugin="", **kwargs):
        if self.current_cell:
            if not plugin:
                plugin = None
            self.current_cell.plugin_identifier = plugin
            self.save_layout()

    def dispatch_publish(self, **kwargs):
        self.view_config.publish()
        return HttpResponse("<html><script>parent.location.reload()</script>Published.</html>")

    def dispatch_revert(self, **kwargs):
        self.view_config.revert()
        return HttpResponse("<html><script>parent.location.reload()</script>Reverted.</html>")
Example #13
0
class EditorView(TemplateView):
    template_name = "shoop/xtheme/editor.jinja"
    xtheme_injection = False  # We don't need the editing injection here, so opt-out
    changed = False  # Overridden in `save_layout`

    def _get_default_layout(self):
        try:
            return json.loads(self.request.GET["default_config"])
        except (ValueError, KeyError):
            return None

    def get_context_data(self, **kwargs):  # doccov: ignore
        ctx = super(EditorView, self).get_context_data(**kwargs)
        ctx["layout"] = self.layout
        ctx["csrf_token_str"] = get_token(self.request)
        # ctx["layout_debug"] = pformat(ctx["layout"].serialize())
        ctx["current_cell_coords"] = self.current_cell_coords
        ctx["current_cell"] = self.current_cell
        ctx["form"] = self.form
        ctx["changed"] = self.changed
        ctx["cell_limit"] = ROW_CELL_LIMIT
        return ctx

    def dispatch(self, request, *args, **kwargs):  # doccov: ignore
        if not could_edit(request):
            raise Problem(_("No access to editing"))
        self._populate_vars()
        if self.default_layout:
            self.view_config.save_default_placeholder_layout(
                self.placeholder_name, self.default_layout)
            # We saved the default layout, so get rid of the humongous GET arg and try again
            get_args = dict(self.request.GET.items())
            get_args.pop("default_config", None)
            return HttpResponseRedirect(
                "%s?%s" % (self.request.path, urlencode(get_args)))
        return super(EditorView, self).dispatch(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):  # doccov: ignore
        command = request.POST.get("command")
        if command:
            dispatcher = getattr(self, "dispatch_%s" % command, None)
            if not callable(dispatcher):
                raise Problem(_("Unknown command %s") % command)
            dispatch_kwargs = dict(request.POST.items())
            rv = dispatcher(**dispatch_kwargs)
            if rv:
                return rv
            self.request.method = "GET"  # At this point, we won't want to cause form validation
            self.build_form()  # and it's not a bad idea to rebuild the form
            return super(EditorView, self).get(request, *args, **kwargs)

        if request.POST.get("save") and self.form and self.form.is_valid():
            self.form.save()
            self.save_layout()

        return super(EditorView, self).get(request, *args, **kwargs)

    def _populate_vars(self):
        theme = get_theme_by_identifier(self.request.GET["theme"])
        if not theme:
            raise Problem(_("Unable to determine current theme."))
        self.view_config = ViewConfig(theme=theme,
                                      view_name=self.request.GET["view"],
                                      draft=True)
        self.placeholder_name = self.request.GET["ph"]
        self.default_layout = self._get_default_layout()
        self.layout = self.view_config.get_placeholder_layout(
            placeholder_name=self.placeholder_name,
            default_layout=self.default_layout)
        (x, y) = self.current_cell_coords = (
            int(self.request.GET.get("x", -1)),
            int(self.request.GET.get("y", -1)),
        )
        self.current_cell = self.layout.get_cell(x=x, y=y)
        self.build_form()

    def build_form(self):
        if not self.current_cell:
            self.form = None
            return
        kwargs = {"layout_cell": self.current_cell}
        if self.request.method == "POST":
            kwargs["data"] = self.request.POST
            kwargs["files"] = self.request.FILES
        self.form = LayoutCellFormGroup(**kwargs)

    def save_layout(self, layout=None):
        self.view_config.save_placeholder_layout(
            placeholder_name=self.placeholder_name,
            layout=(layout or self.layout))
        self.changed = True

    def dispatch_add_cell(self, y, **kwargs):
        y = int(y)
        if len(self.layout.rows[y].cells) >= ROW_CELL_LIMIT:
            raise ValueError(
                _("Cannot add more than %d cells in one row.") %
                ROW_CELL_LIMIT)

        if not (0 <= y < len(self.layout.rows)):
            # No need to raise an exception, really.
            # It must have been a honest mistake.
            return
        self.layout.rows[y].add_cell()
        self.save_layout()

    def dispatch_add_row(self, y=None, **kwargs):
        row = self.layout.insert_row(y)
        row.add_cell()  # For convenience, add a cell to the row.
        self.save_layout()

    def dispatch_del_row(self, y, **kwargs):
        self.layout.delete_row(y)
        self.save_layout()

    def dispatch_del_cell(self, x, y, **kwargs):
        self.layout.delete_cell(x, y)
        self.save_layout()

    def dispatch_change_plugin(self, plugin="", **kwargs):
        if self.current_cell:
            if not plugin:
                plugin = None
            self.current_cell.plugin_identifier = plugin
            self.save_layout()

    def dispatch_publish(self, **kwargs):
        self.view_config.publish()
        return HttpResponse(
            "<html><script>parent.location.reload()</script>Published.</html>")

    def dispatch_revert(self, **kwargs):
        self.view_config.revert()
        return HttpResponse(
            "<html><script>parent.location.reload()</script>Reverted.</html>")
Example #14
0
def test_unsaved_vc_reversion():
    vc = ViewConfig(theme=TestTheme(), view_name=printable_gibberish(), draft=True)
    vc.revert()  # No-op, since this has never been saved (but shouldn't crash either)
Example #15
0
def test_load_save_publish():
    view_name = printable_gibberish()
    theme = TestTheme()
    vc = ViewConfig(theme=theme, view_name=view_name, draft=True)
    placeholder_name = "test_ph"
    data = {"dummy": True}
    vc.save_placeholder_layout(placeholder_name, data)
    assert not ViewConfig(
        theme=theme, view_name=view_name,
        draft=False).saved_view_config.get_layout_data(placeholder_name)
    vc.publish()
    with pytest.raises(ValueError):  # Republishment is bad
        vc.publish()
    with pytest.raises(ValueError):  # Editing directly in public is bad
        vc.save_placeholder_layout(placeholder_name, "break all the things")
    with pytest.raises(ValueError):  # Can't quite revert public changes either
        vc.revert()
    assert ViewConfig(
        theme=theme, view_name=view_name,
        draft=False).saved_view_config.get_layout_data(placeholder_name)
Example #16
0
 def get_layout_data(draft):
     # shorthand -- we're going to be doing this a lot in this test case
     return ViewConfig(
         theme=theme, view_name=view_name,
         draft=draft).saved_view_config.get_layout_data(placeholder_name)
Example #17
0
def test_load_save_publish():
    view_name = printable_gibberish()
    theme = TestTheme()
    vc = ViewConfig(theme=theme, view_name=view_name, draft=True)
    placeholder_name = "test_ph"
    data = {"dummy": True}
    vc.save_placeholder_layout(placeholder_name, data)
    assert not ViewConfig(theme=theme, view_name=view_name, draft=False).saved_view_config.get_layout_data(placeholder_name)
    vc.publish()
    with pytest.raises(ValueError):  # Republishment is bad
        vc.publish()
    with pytest.raises(ValueError):  # Editing directly in public is bad
        vc.save_placeholder_layout(placeholder_name, "break all the things")
    with pytest.raises(ValueError):  # Can't quite revert public changes either
        vc.revert()
    assert ViewConfig(theme=theme, view_name=view_name, draft=False).saved_view_config.get_layout_data(placeholder_name)