在Wagtail中如何让外部链接在新窗口打开

10

我最近实现了像这样添加 target="_blank" 到外部链接:

@hooks.register('after_edit_page')
def do_after_page_edit(request, page):
    if hasattr(page, "body"):
        soup = BeautifulSoup(page.body)
        for a in soup.findAll('a'):
            if hasattr(a, "href"):
            a["target"] = "_blank"
        page.body = str(soup)
        page.body = page.body.replace("<html><head></head><body>", "")
        page.body = page.body.replace("</body></html>", "")
        page.body = page.body.replace("></embed>", "/>")
        page.save()

@hooks.register('construct_whitelister_element_rules')
def whitelister_element_rules():
    return {
        'a': attribute_rule({'href': check_url, 'target': True}),
    }

问题:

如何正确并最好地解决这个问题?

  1. Beautiful Soup会干扰输出,添加 html,head和body 标签 - 不要自动添加 HTML、Head 和 Body 标签,使用 Beautiful Soup

  2. 它还会影响嵌入标签 - 如何让BeautifulSoup 4尊重自关闭标签?

  3. 因此,我的固定方式是用空字符串手动替换输出的部分。

2个回答

25

从Wagtail v2.5开始,作为Wagtail富文本处理的一部分,有一个API可以进行此类自定义:Rewrite handlers,使用register_rich_text_features hook。

以下是使用此新API的示例,制作一个重写处理器,将target="_blank"属性设置给所有外部链接:

from django.utils.html import escape
from wagtail.core import hooks
from wagtail.core.rich_text import LinkHandler


class NewWindowExternalLinkHandler(LinkHandler):
    # This specifies to do this override for external links only.
    # Other identifiers are available for other types of links.
    identifier = 'external'

    @classmethod
    def expand_db_attributes(cls, attrs):
        href = attrs["href"]
        # Let's add the target attr, and also rel="noopener" + noreferrer fallback.
        # See https://github.com/whatwg/html/issues/4078.
        return '<a href="%s" target="_blank" rel="noopener noreferrer">' % escape(href)


@hooks.register('register_rich_text_features')
def register_external_link(features):
    features.register_link_type(NewWindowExternalLinkHandler)
在这个示例中,我还添加了rel="noopener"以修复使用target="_blank"时已知的安全问题
与此问题的先前解决方案相比,这种新方法是最可靠的:它完全在服务器端处理,并仅覆盖了如何在网站前端呈现链接而不是存储它们,而且只依赖于文档API而不是内部API或实现细节。

这是否涉及到不在富文本中的链接? - Chris Barry
这仅适用于富文本链接,因为这些链接是Wagtail中直接生成链接标记的唯一链接。对于其他链接,添加target属性只需要编辑模板即可。其他选项,如使用JS也可以工作,但那并不是特定于Wagtail的方式。 - Thibaud Colas

9
一直在尝试解决同一个问题,但使用wagtailhooks无法实现。我的初始解决方案是在base.html中使用过滤器来操作内容。当将用于剪切代码块的过滤器放置在内容块中时,它可以完美地工作,例如:
{{ self.body|cut: ‘ href="http:’}}

上述过滤器删除了一些内容,但不幸的是,“替换”不可用作过滤器(我使用的是Python 3.x)。因此,我的下一个尝试是构建自定义过滤器以创建“替换”作为过滤器选项。长话短说:它部分地起作用了,但仅当内容从原始的“StreamValue”数据类型转换为“字符串”时。这种转换导致显示所有HTML标签的内容,因此替换没有产生有效的HTML。我无法将内容重新转换回StreamValue,并且没有其他Python数据类型可以解决这个问题。最终,JQuery帮我完成了这项工作:
$(document).ready(function(){
$('a[href^="http://"]').attr('target', '_blank');
});        

这段代码会在每个包含‘http://’的链接中添加‘target="_blank"’,以便所有内部链接都保留在现有选项卡中。它需要放置在您的base.html(或类似文件)的末尾,并且在运行之前当然需要加载JQuery。我从这里得到了答案。不知道JQuery是否是正确和最好的方法,但对我来说它的编码量很小,而且非常有效。

1
我绝对不会回避像这样使用jquery,而且看起来它不会影响SEO(不确定为什么会影响,但值得检查)。然而,这似乎也不是100%最佳解决方案。如果您有不同意见,请随时告诉我,我会给您答案 :) 否则,我可能会保持开放状态。 - Chris Barry
1
使用Wagtailhooks操纵链接可能是可行的。然而,在我看来,有几个原因你更愿意使用JQuery:1.钩子中的操作代码将影响您网站的核心,因此如果出现错误,可能会导致整个站点崩溃。如果JQuery出现错误,只有外部链接会在同一标签页中打开,对您的网站没有影响。 2.使用Wagtailhooks来操纵内容并不是其直接预期的范围。否则,可能会有更多相关信息,并且这个问题早就得到了解答。 - MartijnL
1
  1. 代码将如何应对未来的Wagtail版本?它是否能够与您未来的添加一起工作?如果没有绝对必要,为什么要创建潜在的问题?
我的想法是,简单的解决方案大多是最好的,并且更容易在未来进行维护。如果您已经在网站中使用JQuery,那么也可以将其用于外部链接,这并不是什么大问题。
- MartijnL
只是确认一下,你绝对是正确的。这现在是我实现这个的首选方法。 - Chris Barry
3
您还需要添加 $ ('a[href^ =“https://”]') .attr('target','_blank'); 以覆盖 https url。 - kristian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接