def get_output_cache_base_key(self, placeholder_name, instance):
     """
     .. versionadded:: 1.0
        Return the default cache key, both :func:`get_output_cache_key` and :func:`get_output_cache_keys` rely on this.
        By default, this function generates the cache key using :func:`~fluent_contents.cache.get_rendering_cache_key`.
     """
     return get_rendering_cache_key(placeholder_name, instance)
Exemplo n.º 2
0
    def get_output_cache_keys(self, placeholder_name, instance):
        """
        .. versionadded:: 0.9
           Return the possible cache keys for a rendered item.

           This method should be overwritten when implementing a function :func:`set_cached_output` method
           or when implementing a :func:`get_output_cache_key` function.
           By default, this function generates the cache key using :func:`get_output_cache_base_key`.
        """
        base_key = self.get_output_cache_base_key(placeholder_name, instance)
        cachekeys = [
            base_key
        ]

        if self.cache_output_per_site:
            site_ids = list(Site.objects.values_list('pk', flat=True))
            if settings.SITE_ID not in site_ids:
                site_ids.append(settings.SITE_ID)

            base_key = get_rendering_cache_key(placeholder_name, instance)
            cachekeys = ["{0}-s{1}".format(base_key, site_id) for site_id in site_ids]

        if self.cache_output_per_language:
            # Append language code to all keys,
            # have to invalidate a lot more items in memcache
            total_list = []
            for user_language in list(self.cache_supported_language_codes) + ['unsupported']:
                total_list.extend("{0}.{1}".format(base, user_language) for base in cachekeys)
            cachekeys = total_list

        return cachekeys
Exemplo n.º 3
0
 def get_output_cache_base_key(self, placeholder_name, instance):
     """
     .. versionadded:: 1.0
        Return the default cache key, both :func:`get_output_cache_key` and :func:`get_output_cache_keys` rely on this.
        By default, this function generates the cache key using :func:`~fluent_contents.cache.get_rendering_cache_key`.
     """
     return get_rendering_cache_key(placeholder_name, instance)
Exemplo n.º 4
0
    def get_output_cache_keys(self, placeholder_name, instance):
        """
        .. versionadded:: 0.9
           Return the possible cache keys for a rendered item.

           This method should be overwritten when implementing a function :func:`set_cached_output` method
           or when implementing a :func:`get_output_cache_key` function.
           By default, this function generates the cache key using :func:`get_output_cache_base_key`.
        """
        base_key = self.get_output_cache_base_key(placeholder_name, instance)
        cachekeys = [base_key]

        if self.cache_output_per_site:
            site_ids = list(Site.objects.values_list('pk', flat=True))
            if settings.SITE_ID not in site_ids:
                site_ids.append(settings.SITE_ID)

            base_key = get_rendering_cache_key(placeholder_name, instance)
            cachekeys = [
                "{0}-s{1}".format(base_key, site_id) for site_id in site_ids
            ]

        if self.cache_output_per_language:
            # Append language code to all keys,
            # have to invalidate a lot more items in memcache
            total_list = []
            for user_language in list(
                    self.cache_supported_language_codes) + ['unsupported']:
                total_list.extend("{0}.{1}".format(base, user_language)
                                  for base in cachekeys)
            cachekeys = total_list

        return cachekeys
Exemplo n.º 5
0
    def get_cache_keys(self):
        """
        Get a list of all cache keys associated with this model.
        """
        if not self.placeholder_id:
            # TODO: prune old placeholder slot name?
            return []

        placeholder_name = self.placeholder.slot
        return [get_rendering_cache_key(placeholder_name, self)]
 def get_output_cache_key(self, placeholder_name, instance):
     """
     .. versionadded:: 0.9
        Return the default cache key which is used to store a rendered item.
        By default, this function generates the cache key using :func:`~fluent_contents.cache.get_rendering_cache_key`.
     """
     cachekey = get_rendering_cache_key(placeholder_name, instance)
     if self.cache_output_per_site:
         cachekey = "{0}-s{1}".format(cachekey, settings.SITE_ID)
     return cachekey
Exemplo n.º 7
0
    def get_cache_keys(self):
        """
        Get a list of all cache keys associated with this model.
        """
        if not self.placeholder_id:
            # TODO: prune old placeholder slot name?
            return []

        placeholder_name = self.placeholder.slot
        return [get_rendering_cache_key(placeholder_name, self)]
Exemplo n.º 8
0
    def _fetch_cached_output(self, items, result):
        """
        First try to fetch all items from the cache.
        The items are 'non-polymorphic', so only point to their base class.
        If these are found, there is no need to query the derived data from the database.
        """
        if not appsettings.FLUENT_CONTENTS_CACHE_OUTPUT or not self.use_cached_output:
            result.add_remaining_list(items)
            return

        for contentitem in items:
            result.add_ordering(contentitem)
            output = None

            try:
                plugin = contentitem.plugin
            except PluginNotFound as ex:
                result.store_exception(contentitem,
                                       ex)  # Will deal with that later.
                logger.debug("- item #%s has no matching plugin: %s",
                             contentitem.pk, str(ex))
                continue

            # Respect the cache output setting of the plugin
            if self.can_use_cached_output(contentitem):
                result.add_plugin_timeout(plugin)
                output = plugin.get_cached_output(result.placeholder_name,
                                                  contentitem)

                # Support transition to new output format.
                if output is not None and not isinstance(
                        output, ContentItemOutput):
                    output = None
                    logger.debug(
                        "Flushed cached output of {}#{} to store new ContentItemOutput format (key: {})"
                        .format(
                            plugin.type_name,
                            contentitem.pk,
                            get_placeholder_name(contentitem.placeholder),
                        ))

            # For debugging, ignore cached values when the template is updated.
            if output and settings.DEBUG:
                cachekey = get_rendering_cache_key(result.placeholder_name,
                                                   contentitem)
                if is_template_updated(self.request, contentitem, cachekey):
                    output = None

            if output:
                result.store_output(contentitem, output)
            else:
                result.add_remaining(contentitem)
Exemplo n.º 9
0
    def get_output_cache_keys(self, placeholder_name, instance):
        """
        .. versionadded:: 0.9
           Return the possible cache keys for a rendered item.

           This method should be overwritten when implementing a function :func:`set_cached_output` method
           or when implementing a :func:`get_output_cache_key` function.
           By default, this function generates the cache key using :func:`get_output_cache_base_key`.
        """
        base_key = self.get_output_cache_base_key(placeholder_name, instance)
        cachekeys = [
            base_key,
        ]

        if self.cache_output_per_site:
            site_ids = list(Site.objects.values_list('pk', flat=True))
            if settings.SITE_ID not in site_ids:
                site_ids.append(settings.SITE_ID)

            base_key = get_rendering_cache_key(placeholder_name, instance)
            cachekeys = [
                "{0}-s{1}".format(base_key, site_id) for site_id in site_ids
            ]

        if self.cache_output_per_language or self.render_ignore_item_language:
            # Append language code to all keys,
            # have to invalidate a lot more items in memcache.
            # Also added "None" suffix, since get_parent_language_code() may return that.
            # TODO: ideally for render_ignore_item_language, only invalidate all when the fallback language changed.
            total_list = []
            cache_languages = list(
                self.cache_supported_language_codes) + ['unsupported', 'None']

            # All variants of the Placeholder (for full page caching)
            placeholder = instance.placeholder
            total_list.extend(
                get_placeholder_cache_key(placeholder, lc)
                for lc in cache_languages)

            # All variants of the ContentItem in different languages
            for user_language in cache_languages:
                total_list.extend("{0}.{1}".format(base, user_language)
                                  for base in cachekeys)
            cachekeys = total_list

        return cachekeys
Exemplo n.º 10
0
    def _fetch_cached_output(self, items, result):
        """
        First try to fetch all items from the cache.
        The items are 'non-polymorphic', so only point to their base class.
        If these are found, there is no need to query the derived data from the database.
        """
        if not appsettings.FLUENT_CONTENTS_CACHE_OUTPUT or not self.use_cached_output:
            result.add_remaining_list(items)
            return

        for contentitem in items:
            result.add_ordering(contentitem)
            output = None

            try:
                plugin = contentitem.plugin
            except PluginNotFound as ex:
                result.store_exception(contentitem, ex)  # Will deal with that later.
                logger.debug("- item #%s has no matching plugin: %s", contentitem.pk, str(ex))
                continue

            # Respect the cache output setting of the plugin
            if self.can_use_cached_output(contentitem):
                result.add_plugin_timeout(plugin)
                output = plugin.get_cached_output(result.placeholder_name, contentitem)

                # Support transition to new output format.
                if output is not None and not isinstance(output, ContentItemOutput):
                    output = None
                    logger.debug("Flushed cached output of {0}#{1} to store new ContentItemOutput format (key: {2})".format(
                        plugin.type_name,
                        contentitem.pk,
                        get_placeholder_name(contentitem.placeholder)
                    ))

            # For debugging, ignore cached values when the template is updated.
            if output and settings.DEBUG:
                cachekey = get_rendering_cache_key(result.placeholder_name, contentitem)
                if is_template_updated(self.request, contentitem, cachekey):
                    output = None

            if output:
                result.store_output(contentitem, output)
            else:
                result.add_remaining(contentitem)
    def get_output_cache_keys(self, placeholder_name, instance):
        """
        .. versionadded:: 0.9
           Return the possible cache keys for a rendered item.

           This method should be overwritten when implementing a function :func:`set_cached_output` method
           or implementing a :func:`get_output_cache_key` function which can return multiple results.
           By default, this function generates the cache key using :func:`get_output_cache_key`.
        """
        if self.cache_output_per_site:
            site_ids = list(Site.objects.values_list('pk', flat=True))
            if settings.SITE_ID not in site_ids:
                site_ids.append(settings.SITE_ID)

            base_key = get_rendering_cache_key(placeholder_name, instance)
            return ["{0}-s{1}".format(base_key, site_id) for site_id in site_ids]
        else:
            return [
                self.get_output_cache_key(placeholder_name, instance)
            ]
    def get_output_cache_keys(self, placeholder_name, instance):
        """
        .. versionadded:: 0.9
           Return the possible cache keys for a rendered item.

           This method should be overwritten when implementing a function :func:`set_cached_output` method
           or when implementing a :func:`get_output_cache_key` function.
           By default, this function generates the cache key using :func:`get_output_cache_base_key`.
        """
        base_key = self.get_output_cache_base_key(placeholder_name, instance)
        cachekeys = [
            base_key,
        ]

        if self.cache_output_per_site:
            site_ids = list(Site.objects.values_list('pk', flat=True))
            if settings.SITE_ID not in site_ids:
                site_ids.append(settings.SITE_ID)

            base_key = get_rendering_cache_key(placeholder_name, instance)
            cachekeys = ["{0}-s{1}".format(base_key, site_id) for site_id in site_ids]

        if self.cache_output_per_language or self.render_ignore_item_language:
            # Append language code to all keys,
            # have to invalidate a lot more items in memcache.
            # Also added "None" suffix, since get_parent_language_code() may return that.
            # TODO: ideally for render_ignore_item_language, only invalidate all when the fallback language changed.
            total_list = []
            cache_languages = list(self.cache_supported_language_codes) + ['unsupported', 'None']

            # All variants of the Placeholder (for full page caching)
            placeholder = instance.placeholder
            total_list.extend(get_placeholder_cache_key(placeholder, lc) for lc in cache_languages)

            # All variants of the ContentItem in different languages
            for user_language in cache_languages:
                total_list.extend("{0}.{1}".format(base, user_language) for base in cachekeys)
            cachekeys = total_list

        return cachekeys
Exemplo n.º 13
0
    def get_output_cache_keys(self, placeholder_name, instance):
        """
        .. versionadded:: 0.9
           Return the possible cache keys for a rendered item.

           This method should be overwritten when implementing a function :func:`set_cached_output` method
           or when implementing a :func:`get_output_cache_key` function.
           By default, this function generates the cache key using :func:`get_output_cache_base_key`.
        """
        base_key = self.get_output_cache_base_key(placeholder_name, instance)

        if self.cache_output_per_site:
            site_ids = list(Site.objects.values_list('pk', flat=True))
            if settings.SITE_ID not in site_ids:
                site_ids.append(settings.SITE_ID)

            base_key = get_rendering_cache_key(placeholder_name, instance)
            return [
                "{0}-s{1}".format(base_key, site_id) for site_id in site_ids
            ]
        else:
            return [base_key]
Exemplo n.º 14
0
def _render_items(request, placeholder, items, parent_object=None, template_name=None, cachable=False):
    edit_mode = is_edit_mode(request)
    item_output = {}
    output_ordering = []
    all_cacheable = True
    all_timeout = DEFAULT_TIMEOUT

    placeholder_cache_name = "@global@" if placeholder is None else placeholder.slot

    if not hasattr(items, "non_polymorphic"):
        # The items is either a list of manually created items, or it's a QuerySet.
        # Can't prevent reading the subclasses only, so don't bother with caching here.
        remaining_items = items
        output_ordering = [item.pk or id(item) for item in items]
    else:
        # Unless it was done before, disable polymorphic effects.
        if not items.polymorphic_disabled:
            items = items.non_polymorphic()

        # First try to fetch all items non-polymorphic from memcache
        # If these are found, there is no need to query the derived data from the database.
        remaining_items = []
        for i, contentitem in enumerate(items):
            output_ordering.append(contentitem.pk)
            output = None
            # Respect the cache output setting of the plugin
            if appsettings.FLUENT_CONTENTS_CACHE_OUTPUT:
                try:
                    plugin = contentitem.plugin
                except PluginNotFound:
                    pass
                else:
                    if plugin.cache_output and contentitem.pk:
                        output = plugin.get_cached_output(placeholder_cache_name, contentitem)
                        all_timeout = _min_timeout(all_timeout, plugin.cache_timeout)

                        # Support transition to new output format.
                        if output is not None and not isinstance(output, ContentItemOutput):
                            output = None
                            logger.debug(
                                "Flushed cached output of {0}#{1} to store new ContentItemOutput format (key: {2})".format(
                                    plugin.type_name, contentitem.pk, placeholder_cache_name
                                )
                            )

            # For debugging, ignore cached values when the template is updated.
            if output and settings.DEBUG:
                cachekey = get_rendering_cache_key(placeholder_cache_name, contentitem)
                if _is_template_updated(request, contentitem, cachekey):
                    output = None

            if output:
                item_output[contentitem.pk] = output
            else:
                remaining_items.append(contentitem)

        # Fetch derived table data for all objects not found in memcached
        if remaining_items:
            remaining_items = items.get_real_instances(remaining_items)

    # See if the queryset contained anything.
    # This test is moved here, to prevent earlier query execution.
    if not items:
        placeholder_name = _get_placeholder_name(placeholder)
        logger.debug("- no items in placeholder '%s'", placeholder_name)
        return ContentItemOutput(mark_safe(u"<!-- no items in placeholder '{0}' -->".format(escape(placeholder_name))))
    elif remaining_items:
        # Render remaining items
        for contentitem in remaining_items:
            try:
                plugin = contentitem.plugin
            except PluginNotFound as e:
                output = ContentItemOutput(mark_safe(u"<!-- error: {0} -->\n".format(str(e))))
                logger.debug("- item #%s has no matching plugin: %s", contentitem.pk, str(e))
            else:
                if plugin.render_ignore_item_language or (plugin.cache_output and plugin.cache_output_per_language):
                    # Render the template in the current language.
                    # The cache also stores the output under the current language code.
                    #
                    # It would make sense to apply this for fallback content too,
                    # but that would be ambiguous however because the parent_object could also be a fallback,
                    # and that case can't be detected here. Hence, better be explicit when desiring multi-lingual content.
                    render_language = get_language()  # Avoid switching the content,
                else:
                    # Render the template in the ContentItem language.
                    # This makes sure that {% trans %} tag output matches the language of the model field data.
                    render_language = contentitem.language_code

                with smart_override(render_language):
                    # Plugin output is likely HTML, but it should be placed in mark_safe() to raise awareness about escaping.
                    # This is just like Django's Input.render() and unlike Node.render().
                    output = plugin._render_contentitem(request, contentitem)

                if (
                    appsettings.FLUENT_CONTENTS_CACHE_OUTPUT
                    and plugin.cache_output
                    and output.cacheable
                    and contentitem.pk
                ):
                    # Cache the output
                    contentitem.plugin.set_cached_output(placeholder_cache_name, contentitem, output)
                else:
                    # Item blocks caching the complete placeholder.
                    all_cacheable = False

                    if appsettings.FLUENT_CONTENTS_CACHE_OUTPUT:
                        logger.debug("- item #%s is NOT cachable! Prevented by %r", contentitem.pk, plugin)

                if edit_mode:
                    output.html = _wrap_contentitem_output(output.html, contentitem)

            item_id = contentitem.pk or id(contentitem)
            item_output[item_id] = output

    # Order all rendered items in the correct sequence.  The derived tables could be truncated/reset,
    # so the base class model indexes don't necessary match with the derived indexes. Hence the dict + KeyError handling.
    #
    # The media is also collected in the same ordering, in case it's handled by django-compressor for example.
    output_ordered = []
    merged_media = Media()
    for pk in output_ordering:
        try:
            output = item_output[pk]
            output_ordered.append(output.html)
            _add_media(merged_media, output.media)
        except KeyError:
            # The get_real_instances() didn't return an item for the derived table. This happens when either:
            # - that table is truncated/reset, while there is still an entry in the base ContentItem table.
            #   A query at the derived table happens every time the page is being rendered.
            # - the model was completely removed which means there is also a stale ContentType object.
            item = next(item for item in items if item.pk == pk)
            try:
                class_name = item.plugin.type_name
            except PluginNotFound:
                # Derived table isn't there because the model has been removed.
                # There is a stale ContentType object, no plugin associated or loaded.
                class_name = "content type is stale"

            output_ordered.append(
                u"<!-- Missing derived model for ContentItem #{id}: {cls}. -->\n".format(id=pk, cls=class_name)
            )
            logger.warning("Missing derived model for ContentItem #{id}: {cls}.".format(id=pk, cls=class_name))
            pass

    # Combine all rendered items. Allow rendering the items with a template,
    # to inserting separators or nice start/end code.
    if not template_name:
        merged_output = mark_safe("".join(output_ordered))
    else:
        context = {
            "contentitems": list(zip(items, output_ordered)),
            "parent_object": parent_object,  # Can be None
            "edit_mode": edit_mode,
        }
        merged_output = render_to_string(template_name, context, context_instance=RequestContext(request))

        # By default, cachable is False for templates.
        # Template name is ambiguous, can't reliable expire.
        # Nor can be determined whether the template is consistent or not cacheable.

    if not cachable:
        all_cacheable = False

    return ContentItemOutput(merged_output, merged_media, cacheable=all_cacheable, cache_timeout=all_timeout)
Exemplo n.º 15
0
def _render_items(request, placeholder, items, template_name=None):
    edit_mode = is_edit_mode(request)
    output = {}
    output_ordering = []
    placeholder_cache_name = '@global@' if placeholder is None else placeholder.slot

    if not hasattr(items, "non_polymorphic"):
        # The items is either a list of manually created items, or it's a QuerySet.
        # Can't prevent reading the subclasses only, so don't bother with caching here.
        remaining_items = items
        output_ordering = [item.pk or id(item) for item in items]
    else:
        items = items.non_polymorphic()

        # First try to fetch all items non-polymorphic from memcache
        # If these are found, there is no need to query the derived data from the database.
        remaining_items = []
        for i, contentitem in enumerate(items):
            output_ordering.append(contentitem.pk)
            html = None
            try:
                # Respect the cache output setting of the plugin
                if appsettings.FLUENT_CONTENTS_CACHE_OUTPUT and contentitem.plugin.cache_output and contentitem.pk:
                    html = contentitem.plugin.get_cached_output(placeholder_cache_name, contentitem)
            except PluginNotFound:
                pass

            # For debugging, ignore cached values when the template is updated.
            if html and settings.DEBUG:
                cachekey = get_rendering_cache_key(placeholder_cache_name, contentitem)
                if _is_template_updated(request, contentitem, cachekey):
                    html = None

            if html:
                output[contentitem.pk] = html
            else:
                remaining_items.append(contentitem)

        # Fetch derived table data for all objects not found in memcached
        if remaining_items:
            remaining_items = items.get_real_instances(remaining_items)

    # See if the queryset contained anything.
    # This test is moved here, to prevent earlier query execution.
    if not items:
        return u"<!-- no items in placeholder '{0}' -->".format(escape(_get_placeholder_name(placeholder)))
    elif remaining_items:
        # Render remaining items
        for contentitem in remaining_items:
            try:
                plugin = contentitem.plugin
            except PluginNotFound as e:
                html = '<!-- error: {0} -->\n'.format(str(e))
            else:
                # Plugin output is likely HTML, but it should be placed in mark_safe() to raise awareness about escaping.
                # This is just like Django's Input.render() and unlike Node.render().
                html = conditional_escape(plugin._render_contentitem(request, contentitem))

                if appsettings.FLUENT_CONTENTS_CACHE_OUTPUT and plugin.cache_output and contentitem.pk:
                    contentitem.plugin.set_cached_output(placeholder_cache_name, contentitem, html)

                if edit_mode:
                    html = _wrap_contentitem_output(html, contentitem)

            output[contentitem.pk or id(contentitem)] = html

    # Order all rendered items in the correct sequence.  The derived tables should be truncated/reset,
    # so the base class model indexes don't necessary match with the derived indexes.
    output_ordered = []
    for pk in output_ordering:
        try:
            output_ordered.append(output[pk])
        except KeyError:
            # The get_real_instances() didn't return an item for the derived table. This happens when either:
            # - that table is truncated/reset, while there is still an entry in the base ContentItem table.
            #   A query at the derived table happens every time the page is being rendered.
            # - the model was completely removed which means there is also a stale ContentType object.
            item = next(item for item in items if item.pk == pk)
            try:
                class_name = item.plugin.type_name
            except PluginNotFound:
                # Derived table isn't there because the model has been removed.
                # There is a stale ContentType object, no plugin associated or loaded.
                class_name = 'content type is stale'

            output_ordered.append("<!-- Missing derived model for ContentItem #{id}: {cls}. -->\n".format(id=pk, cls=class_name))
            logger.warning("Missing derived model for ContentItem #{id}: {cls}.".format(id=pk, cls=class_name))
            pass

    # Combine all rendered items. Allow rendering the items with a template,
    # to inserting seperators or nice start/end code.
    if not template_name:
        return mark_safe(''.join(output_ordered))
    else:
        context = {
            'contentitems': zip(items, output_ordered),
            'edit_mode': edit_mode,
        }
        return render_to_string(template_name, context, context_instance=RequestContext(request))
Exemplo n.º 16
0
def _render_items(request, placeholder, items, parent_object=None, template_name=None):
    edit_mode = is_edit_mode(request)
    item_output = {}
    output_ordering = []
    placeholder_cache_name = '@global@' if placeholder is None else placeholder.slot

    if not hasattr(items, "non_polymorphic"):
        # The items is either a list of manually created items, or it's a QuerySet.
        # Can't prevent reading the subclasses only, so don't bother with caching here.
        remaining_items = items
        output_ordering = [item.pk or id(item) for item in items]
    else:
        items = items.non_polymorphic()

        # First try to fetch all items non-polymorphic from memcache
        # If these are found, there is no need to query the derived data from the database.
        remaining_items = []
        for i, contentitem in enumerate(items):
            output_ordering.append(contentitem.pk)
            output = None
            try:
                # Respect the cache output setting of the plugin
                if appsettings.FLUENT_CONTENTS_CACHE_OUTPUT and contentitem.plugin.cache_output and contentitem.pk:
                    output = contentitem.plugin.get_cached_output(placeholder_cache_name, contentitem)

                    # Support transition to new output format.
                    if not isinstance(output, ContentItemOutput):
                        output = None
                        logger.debug("Flushed cached output of {0}#{1} to store new format (key: {2}) ".format(contentitem.plugin.type_name, contentitem.pk, placeholder_cache_name))
            except PluginNotFound:
                pass

            # For debugging, ignore cached values when the template is updated.
            if output and settings.DEBUG:
                cachekey = get_rendering_cache_key(placeholder_cache_name, contentitem)
                if _is_template_updated(request, contentitem, cachekey):
                    output = None

            if output:
                item_output[contentitem.pk] = output
            else:
                remaining_items.append(contentitem)

        # Fetch derived table data for all objects not found in memcached
        if remaining_items:
            remaining_items = items.get_real_instances(remaining_items)

    # See if the queryset contained anything.
    # This test is moved here, to prevent earlier query execution.
    if not items:
        return ContentItemOutput(mark_safe(u"<!-- no items in placeholder '{0}' -->".format(escape(_get_placeholder_name(placeholder)))))
    elif remaining_items:
        # Render remaining items
        for contentitem in remaining_items:
            try:
                plugin = contentitem.plugin
            except PluginNotFound as e:
                output = ContentItemOutput(mark_safe(u'<!-- error: {0} -->\n'.format(str(e))))
            else:
                # Always try to render the template in the ContentItem language.
                # This makes sure that {% trans %} tags function properly if the language happens to be different.
                with smart_override(contentitem.language_code):
                    # Plugin output is likely HTML, but it should be placed in mark_safe() to raise awareness about escaping.
                    # This is just like Django's Input.render() and unlike Node.render().
                    output = plugin._render_contentitem(request, contentitem)

                if appsettings.FLUENT_CONTENTS_CACHE_OUTPUT and plugin.cache_output and contentitem.pk:
                    contentitem.plugin.set_cached_output(placeholder_cache_name, contentitem, output)

                if edit_mode:
                    output.html = _wrap_contentitem_output(output.html, contentitem)

            item_id = contentitem.pk or id(contentitem)
            item_output[item_id] = output

    # Order all rendered items in the correct sequence.  The derived tables could be truncated/reset,
    # so the base class model indexes don't necessary match with the derived indexes. Hence the dict + KeyError handling.
    #
    # The media is also collected in the same ordering, in case it's handled by django-compressor for example.
    output_ordered = []
    merged_media = Media()
    for pk in output_ordering:
        try:
            output = item_output[pk]
            output_ordered.append(output.html)
            _add_media(merged_media, output.media)
        except KeyError:
            # The get_real_instances() didn't return an item for the derived table. This happens when either:
            # - that table is truncated/reset, while there is still an entry in the base ContentItem table.
            #   A query at the derived table happens every time the page is being rendered.
            # - the model was completely removed which means there is also a stale ContentType object.
            item = next(item for item in items if item.pk == pk)
            try:
                class_name = item.plugin.type_name
            except PluginNotFound:
                # Derived table isn't there because the model has been removed.
                # There is a stale ContentType object, no plugin associated or loaded.
                class_name = 'content type is stale'

            output_ordered.append(u"<!-- Missing derived model for ContentItem #{id}: {cls}. -->\n".format(id=pk, cls=class_name))
            logger.warning("Missing derived model for ContentItem #{id}: {cls}.".format(id=pk, cls=class_name))
            pass


    # Combine all rendered items. Allow rendering the items with a template,
    # to inserting separators or nice start/end code.
    if not template_name:
        merged_output = mark_safe(''.join(output_ordered))
    else:
        context = {
            'contentitems': list(zip(items, output_ordered)),
            'parent_object': parent_object,  # Can be None
            'edit_mode': edit_mode,
        }
        merged_output = render_to_string(template_name, context, context_instance=RequestContext(request))

    return ContentItemOutput(merged_output, merged_media)
Exemplo n.º 17
0
def _render_items(request, placeholder_name, items, template_name=None):
    edit_mode = is_edit_mode(request)
    output = {}
    output_ordering = []

    if not hasattr(items, "non_polymorphic"):
        # Can't deal with caching, pass the whole queryset as (i, item) list.
        remaining_items = items
        output_ordering = (item.pk for item in items)
    else:
        items = items.non_polymorphic()

        # First try to fetch all items non-polymorphic from memcache
        remaining_items = []
        for i, contentitem in enumerate(items):
            output_ordering.append(contentitem.pk)
            html = None
            try:
                # Respect the cache output setting of the plugin
                if contentitem.plugin.cache_output and appsettings.FLUENT_CONTENTS_CACHE_OUTPUT:
                    cachekey = get_rendering_cache_key(placeholder_name, contentitem)
                    html = cache.get(cachekey)
            except PluginNotFound:
                pass

            if html:
                output[contentitem.pk] = html
            else:
                remaining_items.append(contentitem)

        if remaining_items:
            remaining_items = items.get_real_instances(remaining_items)

    # See if the queryset contained anything.
    # This test is moved here, to prevent earlier query execution,
    if not items:
        return u"<!-- no items in placeholder '{0}' -->".format(escape(placeholder_name))
    elif remaining_items:
        # Render remaining items
        for contentitem in remaining_items:
            try:
                plugin = contentitem.plugin
            except PluginNotFound as e:
                html = '<!-- error: {0} -->\n'.format(str(e))
            else:
                # Plugin output is likely HTML, but it should be placed in mark_safe() to raise awareness about escaping.
                # This is just like Input.render() and unlike Node.render().
                html = conditional_escape(plugin._render_contentitem(request, contentitem))

                if plugin.cache_output and appsettings.FLUENT_CONTENTS_CACHE_OUTPUT:
                    cachekey = get_rendering_cache_key(placeholder_name, contentitem)
                    cache.set(cachekey, html)

                if edit_mode:
                    html = _wrap_contentitem_output(html, contentitem)

            output[contentitem.pk] = html

    # Order all rendered items in the correct sequence.  The derived tables should be truncated/reset,
    # so the base class model indexes don't necessary match with the derived indexes.
    output_ordered = []
    for pk in output_ordering:
        try:
            output_ordered.append(output[pk])
        except KeyError:
            # NOTE: if a table is truncated/reset, the base class still exists and causes a query to happen every time.
            item = [item for item in items if item.pk == pk][0]
            logger.warning("Missing derived model for ContentItem #{id}: {cls}.".format(id=pk, cls=item.plugin.type_name))
            pass

    # Combine all rendered items. Allow rendering the items with a template,
    # to inserting seperators or nice start/end code.
    if not template_name:
        return mark_safe(''.join(output_ordered))
    else:
        context = {
            'contentitems': zip(items, output_ordered),
            'edit_mode': edit_mode,
        }
        return render_to_string(template_name, context, context_instance=RequestContext(request))
Exemplo n.º 18
0
def _render_items(request, placeholder, items, template_name=None):
    edit_mode = is_edit_mode(request)
    item_output = {}
    output_ordering = []
    placeholder_cache_name = '@global@' if placeholder is None else placeholder.slot

    if not hasattr(items, "non_polymorphic"):
        # The items is either a list of manually created items, or it's a QuerySet.
        # Can't prevent reading the subclasses only, so don't bother with caching here.
        remaining_items = items
        output_ordering = [item.pk or id(item) for item in items]
    else:
        items = items.non_polymorphic()

        # First try to fetch all items non-polymorphic from memcache
        # If these are found, there is no need to query the derived data from the database.
        remaining_items = []
        for i, contentitem in enumerate(items):
            output_ordering.append(contentitem.pk)
            output = None
            try:
                # Respect the cache output setting of the plugin
                if appsettings.FLUENT_CONTENTS_CACHE_OUTPUT and contentitem.plugin.cache_output and contentitem.pk:
                    output = contentitem.plugin.get_cached_output(
                        placeholder_cache_name, contentitem)

                    # Support transition to new output format.
                    if not isinstance(output, ContentItemOutput):
                        output = None
                        logger.debug(
                            "Flushed cached output of {0}#{1} to store new format (key: {2}) "
                            .format(contentitem.plugin.type_name,
                                    contentitem.pk, placeholder_cache_name))
            except PluginNotFound:
                pass

            # For debugging, ignore cached values when the template is updated.
            if output and settings.DEBUG:
                cachekey = get_rendering_cache_key(placeholder_cache_name,
                                                   contentitem)
                if _is_template_updated(request, contentitem, cachekey):
                    output = None

            if output:
                item_output[contentitem.pk] = output
            else:
                remaining_items.append(contentitem)

        # Fetch derived table data for all objects not found in memcached
        if remaining_items:
            remaining_items = items.get_real_instances(remaining_items)

    # See if the queryset contained anything.
    # This test is moved here, to prevent earlier query execution.
    if not items:
        return ContentItemOutput(
            mark_safe(u"<!-- no items in placeholder '{0}' -->".format(
                escape(_get_placeholder_name(placeholder)))))
    elif remaining_items:
        # Render remaining items
        for contentitem in remaining_items:
            try:
                plugin = contentitem.plugin
            except PluginNotFound as e:
                output = ContentItemOutput(
                    mark_safe(u'<!-- error: {0} -->\n'.format(str(e))))
            else:
                # Plugin output is likely HTML, but it should be placed in mark_safe() to raise awareness about escaping.
                # This is just like Django's Input.render() and unlike Node.render().
                output = plugin._render_contentitem(request, contentitem)

                if appsettings.FLUENT_CONTENTS_CACHE_OUTPUT and plugin.cache_output and contentitem.pk:
                    contentitem.plugin.set_cached_output(
                        placeholder_cache_name, contentitem, output)

                if edit_mode:
                    output.html = _wrap_contentitem_output(
                        output.html, contentitem)

            item_id = contentitem.pk or id(contentitem)
            item_output[item_id] = output

    # Order all rendered items in the correct sequence.  The derived tables could be truncated/reset,
    # so the base class model indexes don't necessary match with the derived indexes. Hence the dict + KeyError handling.
    #
    # The media is also collected in the same ordering, in case it's handled by django-compressor for example.
    output_ordered = []
    merged_media = Media()
    for pk in output_ordering:
        try:
            output = item_output[pk]
            output_ordered.append(output.html)
            _add_media(merged_media, output.media)
        except KeyError:
            # The get_real_instances() didn't return an item for the derived table. This happens when either:
            # - that table is truncated/reset, while there is still an entry in the base ContentItem table.
            #   A query at the derived table happens every time the page is being rendered.
            # - the model was completely removed which means there is also a stale ContentType object.
            item = next(item for item in items if item.pk == pk)
            try:
                class_name = item.plugin.type_name
            except PluginNotFound:
                # Derived table isn't there because the model has been removed.
                # There is a stale ContentType object, no plugin associated or loaded.
                class_name = 'content type is stale'

            output_ordered.append(
                u"<!-- Missing derived model for ContentItem #{id}: {cls}. -->\n"
                .format(id=pk, cls=class_name))
            logger.warning(
                "Missing derived model for ContentItem #{id}: {cls}.".format(
                    id=pk, cls=class_name))
            pass

    # Combine all rendered items. Allow rendering the items with a template,
    # to inserting separators or nice start/end code.
    if not template_name:
        merged_output = mark_safe(''.join(output_ordered))
    else:
        context = {
            'contentitems': zip(items, output_ordered),
            'edit_mode': edit_mode,
        }
        merged_output = render_to_string(
            template_name, context, context_instance=RequestContext(request))

    return ContentItemOutput(merged_output, merged_media)
Exemplo n.º 19
0
def _render_items(request, placeholder_name, items, template_name=None):
    edit_mode = is_edit_mode(request)
    output = {}
    output_ordering = []

    if not hasattr(items, "non_polymorphic"):
        # The items is either a list of manually created items, or it's a QuerySet.
        # Can't prevent reading the subclasses only, so don't bother with caching here.
        remaining_items = items
        output_ordering = [item.pk or id(item) for item in items]
    else:
        items = items.non_polymorphic()

        # First try to fetch all items non-polymorphic from memcache
        # If these are found, there is no need to query the derived data from the database.
        remaining_items = []
        for i, contentitem in enumerate(items):
            output_ordering.append(contentitem.pk)
            html = None
            try:
                # Respect the cache output setting of the plugin
                if contentitem.plugin.cache_output and contentitem.pk and appsettings.FLUENT_CONTENTS_CACHE_OUTPUT:
                    cachekey = get_rendering_cache_key(placeholder_name,
                                                       contentitem)
                    html = cache.get(cachekey)
            except PluginNotFound:
                pass

            if html:
                output[contentitem.pk] = html
            else:
                remaining_items.append(contentitem)

        # Fetch derived table data for all objects not found in memcached
        if remaining_items:
            remaining_items = items.get_real_instances(remaining_items)

    # See if the queryset contained anything.
    # This test is moved here, to prevent earlier query execution.
    if not items:
        return u"<!-- no items in placeholder '{0}' -->".format(
            escape(placeholder_name))
    elif remaining_items:
        # Render remaining items
        for contentitem in remaining_items:
            try:
                plugin = contentitem.plugin
            except PluginNotFound as e:
                html = '<!-- error: {0} -->\n'.format(str(e))
            else:
                # Plugin output is likely HTML, but it should be placed in mark_safe() to raise awareness about escaping.
                # This is just like Django's Input.render() and unlike Node.render().
                html = conditional_escape(
                    plugin._render_contentitem(request, contentitem))

                if plugin.cache_output and contentitem.pk and appsettings.FLUENT_CONTENTS_CACHE_OUTPUT:
                    cachekey = get_rendering_cache_key(placeholder_name,
                                                       contentitem)
                    cache.set(cachekey, html)

                if edit_mode:
                    html = _wrap_contentitem_output(html, contentitem)

            output[contentitem.pk or id(contentitem)] = html

    # Order all rendered items in the correct sequence.  The derived tables should be truncated/reset,
    # so the base class model indexes don't necessary match with the derived indexes.
    output_ordered = []
    for pk in output_ordering:
        try:
            output_ordered.append(output[pk])
        except KeyError:
            # NOTE: if a table is truncated/reset, the base class still exists and causes a query to happen every time.
            item = [item for item in items if item.pk == pk][0]
            logger.warning(
                "Missing derived model for ContentItem #{id}: {cls}.".format(
                    id=pk, cls=item.plugin.type_name))
            pass

    # Combine all rendered items. Allow rendering the items with a template,
    # to inserting seperators or nice start/end code.
    if not template_name:
        return mark_safe(''.join(output_ordered))
    else:
        context = {
            'contentitems': zip(items, output_ordered),
            'edit_mode': edit_mode,
        }
        return render_to_string(template_name,
                                context,
                                context_instance=RequestContext(request))
Exemplo n.º 20
0
def _render_items(request,
                  placeholder,
                  items,
                  parent_object=None,
                  template_name=None,
                  cachable=False):
    edit_mode = is_edit_mode(request)
    item_output = {}
    output_ordering = []
    all_cacheable = True
    all_timeout = DEFAULT_TIMEOUT

    placeholder_cache_name = '@global@' if placeholder is None else placeholder.slot

    if not hasattr(items, "non_polymorphic"):
        # The items is either a list of manually created items, or it's a QuerySet.
        # Can't prevent reading the subclasses only, so don't bother with caching here.
        remaining_items = items
        output_ordering = [item.pk or id(item) for item in items]
    else:
        # Unless it was done before, disable polymorphic effects.
        if not items.polymorphic_disabled:
            items = items.non_polymorphic()

        # First try to fetch all items non-polymorphic from memcache
        # If these are found, there is no need to query the derived data from the database.
        remaining_items = []
        for i, contentitem in enumerate(items):
            output_ordering.append(contentitem.pk)
            output = None
            # Respect the cache output setting of the plugin
            if appsettings.FLUENT_CONTENTS_CACHE_OUTPUT:
                try:
                    plugin = contentitem.plugin
                except PluginNotFound:
                    pass
                else:
                    if plugin.cache_output and contentitem.pk:
                        output = plugin.get_cached_output(
                            placeholder_cache_name, contentitem)
                        all_timeout = _min_timeout(all_timeout,
                                                   plugin.cache_timeout)

                        # Support transition to new output format.
                        if output is not None and not isinstance(
                                output, ContentItemOutput):
                            output = None
                            logger.debug(
                                "Flushed cached output of {0}#{1} to store new ContentItemOutput format (key: {2})"
                                .format(plugin.type_name, contentitem.pk,
                                        placeholder_cache_name))

            # For debugging, ignore cached values when the template is updated.
            if output and settings.DEBUG:
                cachekey = get_rendering_cache_key(placeholder_cache_name,
                                                   contentitem)
                if _is_template_updated(request, contentitem, cachekey):
                    output = None

            if output:
                item_output[contentitem.pk] = output
            else:
                remaining_items.append(contentitem)

        # Fetch derived table data for all objects not found in memcached
        if remaining_items:
            remaining_items = items.get_real_instances(remaining_items)

    # See if the queryset contained anything.
    # This test is moved here, to prevent earlier query execution.
    if not items:
        placeholder_name = _get_placeholder_name(placeholder)
        logger.debug("- no items in placeholder '%s'", placeholder_name)
        return ContentItemOutput(
            mark_safe(u"<!-- no items in placeholder '{0}' -->".format(
                escape(placeholder_name))))
    elif remaining_items:
        # Render remaining items
        for contentitem in remaining_items:
            try:
                plugin = contentitem.plugin
            except PluginNotFound as e:
                output = ContentItemOutput(
                    mark_safe(u'<!-- error: {0} -->\n'.format(str(e))))
                logger.debug("- item #%s has no matching plugin: %s",
                             contentitem.pk, str(e))
            else:
                if plugin.render_ignore_item_language \
                or (plugin.cache_output and plugin.cache_output_per_language):
                    # Render the template in the current language.
                    # The cache also stores the output under the current language code.
                    #
                    # It would make sense to apply this for fallback content too,
                    # but that would be ambiguous however because the parent_object could also be a fallback,
                    # and that case can't be detected here. Hence, better be explicit when desiring multi-lingual content.
                    render_language = get_language(
                    )  # Avoid switching the content,
                else:
                    # Render the template in the ContentItem language.
                    # This makes sure that {% trans %} tag output matches the language of the model field data.
                    render_language = contentitem.language_code

                with smart_override(render_language):
                    # Plugin output is likely HTML, but it should be placed in mark_safe() to raise awareness about escaping.
                    # This is just like Django's Input.render() and unlike Node.render().
                    output = plugin._render_contentitem(request, contentitem)

                if appsettings.FLUENT_CONTENTS_CACHE_OUTPUT \
                and plugin.cache_output \
                and output.cacheable \
                and contentitem.pk:
                    # Cache the output
                    contentitem.plugin.set_cached_output(
                        placeholder_cache_name, contentitem, output)
                else:
                    # Item blocks caching the complete placeholder.
                    all_cacheable = False

                    if appsettings.FLUENT_CONTENTS_CACHE_OUTPUT:
                        logger.debug(
                            "- item #%s is NOT cachable! Prevented by %r",
                            contentitem.pk, plugin)

                if edit_mode:
                    output.html = _wrap_contentitem_output(
                        output.html, contentitem)

            item_id = contentitem.pk or id(contentitem)
            item_output[item_id] = output

    # Order all rendered items in the correct sequence.  The derived tables could be truncated/reset,
    # so the base class model indexes don't necessary match with the derived indexes. Hence the dict + KeyError handling.
    #
    # The media is also collected in the same ordering, in case it's handled by django-compressor for example.
    output_ordered = []
    merged_media = Media()
    for pk in output_ordering:
        try:
            output = item_output[pk]
            output_ordered.append(output.html)
            _add_media(merged_media, output.media)
        except KeyError:
            # The get_real_instances() didn't return an item for the derived table. This happens when either:
            # - that table is truncated/reset, while there is still an entry in the base ContentItem table.
            #   A query at the derived table happens every time the page is being rendered.
            # - the model was completely removed which means there is also a stale ContentType object.
            item = next(item for item in items if item.pk == pk)
            try:
                class_name = item.plugin.type_name
            except PluginNotFound:
                # Derived table isn't there because the model has been removed.
                # There is a stale ContentType object, no plugin associated or loaded.
                class_name = 'content type is stale'

            output_ordered.append(
                u"<!-- Missing derived model for ContentItem #{id}: {cls}. -->\n"
                .format(id=pk, cls=class_name))
            logger.warning(
                "Missing derived model for ContentItem #{id}: {cls}.".format(
                    id=pk, cls=class_name))
            pass

    # Combine all rendered items. Allow rendering the items with a template,
    # to inserting separators or nice start/end code.
    if not template_name:
        merged_output = mark_safe(''.join(output_ordered))
    else:
        context = {
            'contentitems': list(zip(items, output_ordered)),
            'parent_object': parent_object,  # Can be None
            'edit_mode': edit_mode,
        }
        merged_output = render_to_string(
            template_name, context, context_instance=RequestContext(request))

        # By default, cachable is False for templates.
        # Template name is ambiguous, can't reliable expire.
        # Nor can be determined whether the template is consistent or not cacheable.

    if not cachable:
        all_cacheable = False

    return ContentItemOutput(merged_output,
                             merged_media,
                             cacheable=all_cacheable,
                             cache_timeout=all_timeout)