Add native lazy loading for images and iframes
This feature is available only in Chrome >= 76 for now. See https://web.dev/native-lazy-loading
This commit is contained in:
parent
937492f6f5
commit
8d8f78241d
11 changed files with 39 additions and 37 deletions
|
@ -137,7 +137,9 @@ func getExtraAttributes(tagName string) ([]string, []string) {
|
|||
case "video", "audio":
|
||||
return []string{"controls"}, []string{"controls"}
|
||||
case "iframe":
|
||||
return []string{"sandbox"}, []string{`sandbox="allow-scripts allow-same-origin allow-popups"`}
|
||||
return []string{"sandbox", "loading"}, []string{`sandbox="allow-scripts allow-same-origin allow-popups"`, `loading="lazy"`}
|
||||
case "img":
|
||||
return []string{"loading"}, []string{`loading="lazy"`}
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ package sanitizer // import "miniflux.app/reader/sanitizer"
|
|||
import "testing"
|
||||
|
||||
func TestValidInput(t *testing.T) {
|
||||
input := `<p>This is a <strong>text</strong> with an image: <img src="http://example.org/" alt="Test">.</p>`
|
||||
input := `<p>This is a <strong>text</strong> with an image: <img src="http://example.org/" alt="Test" loading="lazy">.</p>`
|
||||
output := Sanitize("http://example.org/", input)
|
||||
|
||||
if input != output {
|
||||
|
@ -16,7 +16,7 @@ func TestValidInput(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSelfClosingTags(t *testing.T) {
|
||||
input := `<p>This <br> is a <strong>text</strong> <br/>with an image: <img src="http://example.org/" alt="Test"/>.</p>`
|
||||
input := `<p>This <br> is a <strong>text</strong> <br/>with an image: <img src="http://example.org/" alt="Test" loading="lazy"/>.</p>`
|
||||
output := Sanitize("http://example.org/", input)
|
||||
|
||||
if input != output {
|
||||
|
@ -35,7 +35,7 @@ func TestTable(t *testing.T) {
|
|||
|
||||
func TestRelativeURL(t *testing.T) {
|
||||
input := `This <a href="/test.html">link is relative</a> and this image: <img src="../folder/image.png"/>`
|
||||
expected := `This <a href="http://example.org/test.html" rel="noopener noreferrer" target="_blank" referrerpolicy="no-referrer">link is relative</a> and this image: <img src="http://example.org/folder/image.png"/>`
|
||||
expected := `This <a href="http://example.org/test.html" rel="noopener noreferrer" target="_blank" referrerpolicy="no-referrer">link is relative</a> and this image: <img src="http://example.org/folder/image.png" loading="lazy"/>`
|
||||
output := Sanitize("http://example.org/", input)
|
||||
|
||||
if expected != output {
|
||||
|
@ -165,7 +165,7 @@ func TestEspaceAttributes(t *testing.T) {
|
|||
|
||||
func TestReplaceYoutubeURL(t *testing.T) {
|
||||
input := `<iframe src="http://www.youtube.com/embed/test123?version=3&rel=1&fs=1&autohide=2&showsearch=0&showinfo=1&iv_load_policy=1&wmode=transparent"></iframe>`
|
||||
expected := `<iframe src="https://www.youtube-nocookie.com/embed/test123?version=3&rel=1&fs=1&autohide=2&showsearch=0&showinfo=1&iv_load_policy=1&wmode=transparent" sandbox="allow-scripts allow-same-origin allow-popups"></iframe>`
|
||||
expected := `<iframe src="https://www.youtube-nocookie.com/embed/test123?version=3&rel=1&fs=1&autohide=2&showsearch=0&showinfo=1&iv_load_policy=1&wmode=transparent" sandbox="allow-scripts allow-same-origin allow-popups" loading="lazy"></iframe>`
|
||||
output := Sanitize("http://example.org/", input)
|
||||
|
||||
if expected != output {
|
||||
|
@ -175,7 +175,7 @@ func TestReplaceYoutubeURL(t *testing.T) {
|
|||
|
||||
func TestReplaceSecureYoutubeURL(t *testing.T) {
|
||||
input := `<iframe src="https://www.youtube.com/embed/test123"></iframe>`
|
||||
expected := `<iframe src="https://www.youtube-nocookie.com/embed/test123" sandbox="allow-scripts allow-same-origin allow-popups"></iframe>`
|
||||
expected := `<iframe src="https://www.youtube-nocookie.com/embed/test123" sandbox="allow-scripts allow-same-origin allow-popups" loading="lazy"></iframe>`
|
||||
output := Sanitize("http://example.org/", input)
|
||||
|
||||
if expected != output {
|
||||
|
@ -185,7 +185,7 @@ func TestReplaceSecureYoutubeURL(t *testing.T) {
|
|||
|
||||
func TestReplaceSecureYoutubeURLWithParameters(t *testing.T) {
|
||||
input := `<iframe src="https://www.youtube.com/embed/test123?rel=0&controls=0"></iframe>`
|
||||
expected := `<iframe src="https://www.youtube-nocookie.com/embed/test123?rel=0&controls=0" sandbox="allow-scripts allow-same-origin allow-popups"></iframe>`
|
||||
expected := `<iframe src="https://www.youtube-nocookie.com/embed/test123?rel=0&controls=0" sandbox="allow-scripts allow-same-origin allow-popups" loading="lazy"></iframe>`
|
||||
output := Sanitize("http://example.org/", input)
|
||||
|
||||
if expected != output {
|
||||
|
@ -195,7 +195,7 @@ func TestReplaceSecureYoutubeURLWithParameters(t *testing.T) {
|
|||
|
||||
func TestReplaceYoutubeURLAlreadyReplaced(t *testing.T) {
|
||||
input := `<iframe src="https://www.youtube-nocookie.com/embed/test123?rel=0&controls=0" sandbox="allow-scripts allow-same-origin"></iframe>`
|
||||
expected := `<iframe src="https://www.youtube-nocookie.com/embed/test123?rel=0&controls=0" sandbox="allow-scripts allow-same-origin allow-popups"></iframe>`
|
||||
expected := `<iframe src="https://www.youtube-nocookie.com/embed/test123?rel=0&controls=0" sandbox="allow-scripts allow-same-origin allow-popups" loading="lazy"></iframe>`
|
||||
output := Sanitize("http://example.org/", input)
|
||||
|
||||
if expected != output {
|
||||
|
@ -205,7 +205,7 @@ func TestReplaceYoutubeURLAlreadyReplaced(t *testing.T) {
|
|||
|
||||
func TestReplaceProtocolRelativeYoutubeURL(t *testing.T) {
|
||||
input := `<iframe src="//www.youtube.com/embed/Bf2W84jrGqs" width="560" height="314" allowfullscreen="allowfullscreen"></iframe>`
|
||||
expected := `<iframe src="https://www.youtube-nocookie.com/embed/Bf2W84jrGqs" width="560" height="314" allowfullscreen="allowfullscreen" sandbox="allow-scripts allow-same-origin allow-popups"></iframe>`
|
||||
expected := `<iframe src="https://www.youtube-nocookie.com/embed/Bf2W84jrGqs" width="560" height="314" allowfullscreen="allowfullscreen" sandbox="allow-scripts allow-same-origin allow-popups" loading="lazy"></iframe>`
|
||||
output := Sanitize("http://example.org/", input)
|
||||
|
||||
if expected != output {
|
||||
|
@ -215,7 +215,7 @@ func TestReplaceProtocolRelativeYoutubeURL(t *testing.T) {
|
|||
|
||||
func TestReplaceIframeURL(t *testing.T) {
|
||||
input := `<iframe src="https://player.vimeo.com/video/123456?title=0&byline=0"></iframe>`
|
||||
expected := `<iframe src="https://player.vimeo.com/video/123456?title=0&byline=0" sandbox="allow-scripts allow-same-origin allow-popups"></iframe>`
|
||||
expected := `<iframe src="https://player.vimeo.com/video/123456?title=0&byline=0" sandbox="allow-scripts allow-same-origin allow-popups" loading="lazy"></iframe>`
|
||||
output := Sanitize("http://example.org/", input)
|
||||
|
||||
if expected != output {
|
||||
|
@ -224,7 +224,7 @@ func TestReplaceIframeURL(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestReplaceNoScript(t *testing.T) {
|
||||
input := `<p>Before paragraph.</p><noscript>Inside <code>noscript</code> tag with an image: <img src="http://example.org/" alt="Test"></noscript><p>After paragraph.</p>`
|
||||
input := `<p>Before paragraph.</p><noscript>Inside <code>noscript</code> tag with an image: <img src="http://example.org/" alt="Test" loading="lazy"></noscript><p>After paragraph.</p>`
|
||||
expected := `<p>Before paragraph.</p><p>After paragraph.</p>`
|
||||
output := Sanitize("http://example.org/", input)
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<div class="item-header">
|
||||
<span class="item-title">
|
||||
{{ if ne .Feed.Icon.IconID 0 }}
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" alt="{{ .Feed.Title }}">
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Feed.Title }}">
|
||||
{{ end }}
|
||||
<a href="{{ route "starredEntry" "entryID" .ID }}">{{ .Title }}</a>
|
||||
</span>
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
<div class="item-header">
|
||||
<span class="item-title">
|
||||
{{ if ne .Feed.Icon.IconID 0 }}
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" alt="{{ .Feed.Title }}">
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Feed.Title }}">
|
||||
{{ end }}
|
||||
<a href="{{ route "categoryEntry" "categoryID" .Feed.Category.ID "entryID" .ID }}">{{ .Title }}</a>
|
||||
</span>
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
<div class="entry-meta">
|
||||
<span class="entry-website">
|
||||
{{ if ne .entry.Feed.Icon.IconID 0 }}
|
||||
<img src="{{ route "icon" "iconID" .entry.Feed.Icon.IconID }}" width="16" height="16" alt="{{ .entry.Feed.Title }}">
|
||||
<img src="{{ route "icon" "iconID" .entry.Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .entry.Feed.Title }}">
|
||||
{{ end }}
|
||||
<a href="{{ route "feedEntries" "feedID" .entry.Feed.ID }}">{{ .entry.Feed.Title }}</a>
|
||||
</span>
|
||||
|
@ -105,7 +105,7 @@
|
|||
</div>
|
||||
{{ else if hasPrefix .MimeType "image/" }}
|
||||
<div class="enclosure-image">
|
||||
<img src="{{ proxyURL .URL }}" title="{{ .URL }} ({{ .MimeType }})" alt="{{ .URL }} ({{ .MimeType }})">
|
||||
<img src="{{ proxyURL .URL }}" title="{{ .URL }} ({{ .MimeType }})" loading="lazy" alt="{{ .URL }} ({{ .MimeType }})">
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
<div class="item-header">
|
||||
<span class="item-title">
|
||||
{{ if ne .Feed.Icon.IconID 0 }}
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" alt="{{ .Feed.Title }}">
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Feed.Title }}">
|
||||
{{ end }}
|
||||
<a href="{{ route "feedEntry" "feedID" .Feed.ID "entryID" .ID }}">{{ .Title }}</a>
|
||||
</span>
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
<div class="item-header">
|
||||
<span class="item-title">
|
||||
{{ if .Icon }}
|
||||
<img src="{{ route "icon" "iconID" .Icon.IconID }}" width="16" height="16" alt="{{ .Title }}">
|
||||
<img src="{{ route "icon" "iconID" .Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Title }}">
|
||||
{{ end }}
|
||||
{{ if .Disabled }} 🚫 {{ end }}
|
||||
<a href="{{ route "feedEntries" "feedID" .ID }}">{{ .Title }}</a>
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<div class="item-header">
|
||||
<span class="item-title">
|
||||
{{ if ne .Feed.Icon.IconID 0 }}
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" alt="{{ .Feed.Title }}">
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Feed.Title }}">
|
||||
{{ end }}
|
||||
<a href="{{ route "readEntry" "entryID" .ID }}">{{ .Title }}</a>
|
||||
</span>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<div class="item-header">
|
||||
<span class="item-title">
|
||||
{{ if ne .Feed.Icon.IconID 0 }}
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" alt="{{ .Feed.Title }}">
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Feed.Title }}">
|
||||
{{ end }}
|
||||
<a href="{{ route "searchEntry" "entryID" .ID }}?q={{ $.searchQuery }}">{{ .Title }}</a>
|
||||
</span>
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
<div class="item-header">
|
||||
<span class="item-title">
|
||||
{{ if ne .Feed.Icon.IconID 0 }}
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" alt="{{ .Feed.Title }}">
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Feed.Title }}">
|
||||
{{ end }}
|
||||
<a href="{{ route "unreadEntry" "entryID" .ID }}">{{ .Title }}</a>
|
||||
</span>
|
||||
|
|
|
@ -129,7 +129,7 @@ var templateViewsMap = map[string]string{
|
|||
<div class="item-header">
|
||||
<span class="item-title">
|
||||
{{ if ne .Feed.Icon.IconID 0 }}
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" alt="{{ .Feed.Title }}">
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Feed.Title }}">
|
||||
{{ end }}
|
||||
<a href="{{ route "starredEntry" "entryID" .ID }}">{{ .Title }}</a>
|
||||
</span>
|
||||
|
@ -239,7 +239,7 @@ var templateViewsMap = map[string]string{
|
|||
<div class="item-header">
|
||||
<span class="item-title">
|
||||
{{ if ne .Feed.Icon.IconID 0 }}
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" alt="{{ .Feed.Title }}">
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Feed.Title }}">
|
||||
{{ end }}
|
||||
<a href="{{ route "categoryEntry" "categoryID" .Feed.Category.ID "entryID" .ID }}">{{ .Title }}</a>
|
||||
</span>
|
||||
|
@ -636,7 +636,7 @@ var templateViewsMap = map[string]string{
|
|||
<div class="entry-meta">
|
||||
<span class="entry-website">
|
||||
{{ if ne .entry.Feed.Icon.IconID 0 }}
|
||||
<img src="{{ route "icon" "iconID" .entry.Feed.Icon.IconID }}" width="16" height="16" alt="{{ .entry.Feed.Title }}">
|
||||
<img src="{{ route "icon" "iconID" .entry.Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .entry.Feed.Title }}">
|
||||
{{ end }}
|
||||
<a href="{{ route "feedEntries" "feedID" .entry.Feed.ID }}">{{ .entry.Feed.Title }}</a>
|
||||
</span>
|
||||
|
@ -684,7 +684,7 @@ var templateViewsMap = map[string]string{
|
|||
</div>
|
||||
{{ else if hasPrefix .MimeType "image/" }}
|
||||
<div class="enclosure-image">
|
||||
<img src="{{ proxyURL .URL }}" title="{{ .URL }} ({{ .MimeType }})" alt="{{ .URL }} ({{ .MimeType }})">
|
||||
<img src="{{ proxyURL .URL }}" title="{{ .URL }} ({{ .MimeType }})" loading="lazy" alt="{{ .URL }} ({{ .MimeType }})">
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
|
@ -769,7 +769,7 @@ var templateViewsMap = map[string]string{
|
|||
<div class="item-header">
|
||||
<span class="item-title">
|
||||
{{ if ne .Feed.Icon.IconID 0 }}
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" alt="{{ .Feed.Title }}">
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Feed.Title }}">
|
||||
{{ end }}
|
||||
<a href="{{ route "feedEntry" "feedID" .Feed.ID "entryID" .ID }}">{{ .Title }}</a>
|
||||
</span>
|
||||
|
@ -829,7 +829,7 @@ var templateViewsMap = map[string]string{
|
|||
<div class="item-header">
|
||||
<span class="item-title">
|
||||
{{ if .Icon }}
|
||||
<img src="{{ route "icon" "iconID" .Icon.IconID }}" width="16" height="16" alt="{{ .Title }}">
|
||||
<img src="{{ route "icon" "iconID" .Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Title }}">
|
||||
{{ end }}
|
||||
{{ if .Disabled }} 🚫 {{ end }}
|
||||
<a href="{{ route "feedEntries" "feedID" .ID }}">{{ .Title }}</a>
|
||||
|
@ -907,7 +907,7 @@ var templateViewsMap = map[string]string{
|
|||
<div class="item-header">
|
||||
<span class="item-title">
|
||||
{{ if ne .Feed.Icon.IconID 0 }}
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" alt="{{ .Feed.Title }}">
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Feed.Title }}">
|
||||
{{ end }}
|
||||
<a href="{{ route "readEntry" "entryID" .ID }}">{{ .Title }}</a>
|
||||
</span>
|
||||
|
@ -1175,7 +1175,7 @@ var templateViewsMap = map[string]string{
|
|||
<div class="item-header">
|
||||
<span class="item-title">
|
||||
{{ if ne .Feed.Icon.IconID 0 }}
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" alt="{{ .Feed.Title }}">
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Feed.Title }}">
|
||||
{{ end }}
|
||||
<a href="{{ route "searchEntry" "entryID" .ID }}?q={{ $.searchQuery }}">{{ .Title }}</a>
|
||||
</span>
|
||||
|
@ -1370,7 +1370,7 @@ var templateViewsMap = map[string]string{
|
|||
<div class="item-header">
|
||||
<span class="item-title">
|
||||
{{ if ne .Feed.Icon.IconID 0 }}
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" alt="{{ .Feed.Title }}">
|
||||
<img src="{{ route "icon" "iconID" .Feed.Icon.IconID }}" width="16" height="16" loading="lazy" alt="{{ .Feed.Title }}">
|
||||
{{ end }}
|
||||
<a href="{{ route "unreadEntry" "entryID" .ID }}">{{ .Title }}</a>
|
||||
</span>
|
||||
|
@ -1467,25 +1467,25 @@ var templateViewsMap = map[string]string{
|
|||
var templateViewsMapChecksums = map[string]string{
|
||||
"about": "844e3313c33ae31a74b904f6ef5d60299773620d8450da6f760f9f317217c51e",
|
||||
"add_subscription": "a0f1d2bc02b6adc83dbeae593f74d9b936102cd6dd73302cdbec2137cafdcdd9",
|
||||
"bookmark_entries": "609f4b2342152fe495a219a32f17a4528b01807d61f53cee0cbebf728be73c42",
|
||||
"bookmark_entries": "65588da78665699dd3f287f68325e9777d511f1a57fee4131a5bb6d00bb68df8",
|
||||
"categories": "642ee3cddbd825ee6ab5a77caa0d371096b55de0f1bd4ae3055b8c8a70507d8d",
|
||||
"category_entries": "5affb6ddaf73ac7b14d9cc67f7d518d4bb8f280ee6d9f1ad852edd44bad8c7de",
|
||||
"category_entries": "3ec30d2cb97f29514ff61898a4f23d2aa73a24b3468b6d410b1c2d18c8808927",
|
||||
"choose_subscription": "33c04843d7c1b608d034e605e52681822fc6d79bc6b900c04915dd9ebae584e2",
|
||||
"create_category": "6b22b5ce51abf4e225e23a79f81be09a7fb90acb265e93a8faf9446dff74018d",
|
||||
"create_user": "1e940be3afefc0a5c6273bbadcddc1e29811e9548e5227ac2adfe697ca5ce081",
|
||||
"edit_category": "daf073d2944a180ce5aaeb80b597eb69597a50dff55a9a1d6cf7938b48d768cb",
|
||||
"edit_feed": "34aa0d668b3ea1a1b5fa480c20cebeae729b37010af3bb915d2a9eed73d3b996",
|
||||
"edit_user": "f4f99412ba771cfca2a2a42778b023b413c5494e9a287053ba8cf380c2865c5f",
|
||||
"entry": "1626bf4dd3223b2f730865676162aa0a9f0a0e009cdea90f705230542922e0f4",
|
||||
"feed_entries": "4bb6b96ba4d13dbaf22dcf6dd95ae36b6e5a0c99175d502865a164dc68fd4bae",
|
||||
"feeds": "d11fb629921e22bbf6d9ecb1adcc38922fafcee84f81c437abf47209544bd1c5",
|
||||
"history_entries": "9763d2120cfaeb78d406fdc029197fed2f7cfa7682970eeedae82ae79be65519",
|
||||
"entry": "e14434fc6f57963eae26057a18c835d0328af783d41f5af04b03387b4da604be",
|
||||
"feed_entries": "9c70b82f55e4b311eff20be1641733612e3c1b406ce8010861e4c417d97b6dcc",
|
||||
"feeds": "fa2dad422445eca898c1daa4ab742691207a8c0d3c274eed84462bc610d22219",
|
||||
"history_entries": "87e17d39de70eb3fdbc4000326283be610928758eae7924e4b08dcb446f3b6a9",
|
||||
"import": "5eb56cecaa4d369b9acc991a82be7617710c551089a2e99d34ce8b6e5c37df0a",
|
||||
"integrations": "f85b4a48ab1fc13b8ca94bfbbc44bd5e8784f35b26a63ec32cbe82b96b45e008",
|
||||
"login": "2e72d2d4b9786641b696bedbed5e10b04bdfd68254ddbbdb0a53cca621d200c7",
|
||||
"search_entries": "d71849a4f2b0573c7c76ad0ea941812009e9f022de60895987a781d3e6f08a01",
|
||||
"search_entries": "274950d03298c24f3942e209c0faed580a6d57be9cf76a6c236175a7e766ac6a",
|
||||
"sessions": "1b3ec0970a4111b81f86d6ed187bb410f88972e2ede6723b9febcc4c7e5fc921",
|
||||
"settings": "152143e58d057ea6ab3bfd8dd947bfd70685843ca40e40542484b23849746df4",
|
||||
"unread_entries": "5c8c67d69da3e1d9437fdae967206b6dec84b241c806f32373071558f72d05d7",
|
||||
"unread_entries": "e38f7ffce17dfad3151b08cd33771a2cefe8ca9db42df04fc98bd1d675dd6075",
|
||||
"users": "4b56cc76fbcc424e7c870d0efca93bb44dbfcc2a08b685cf799c773fbb8dfb2f",
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue