From 309cd7a38130f6ef82fb1c4b7b9998de12dd7bef Mon Sep 17 00:00:00 2001
From: The OC Guy
Date: Mon, 26 Oct 2026 20:41:15 +0000
Subject: [PATCH] Three content areas: Tips, News, Modules
---
...els-like-a-genuinely-polished-release.html | 4 +-
...v19-and-that-matters-more-than-it-sou.html | 4 +-
index.html | 35 ++++-----
js/feed.js | 72 ++++++-------------
scripts/sync_posts.py | 14 ++--
5 files changed, 50 insertions(+), 79 deletions(-)
diff --git a/blog/odoo-20-community-is-here-and-it-feels-like-a-genuinely-polished-release.html b/blog/odoo-20-community-is-here-and-it-feels-like-a-genuinely-polished-release.html
index 1e59186..d996916 100644
--- a/blog/odoo-20-community-is-here-and-it-feels-like-a-genuinely-polished-release.html
+++ b/blog/odoo-20-community-is-here-and-it-feels-like-a-genuinely-polished-release.html
@@ -23,8 +23,8 @@
The OC Guy
diff --git a/blog/paraguay-s-odoo-localization-is-updated-to-v19-and-that-matters-more-than-it-sou.html b/blog/paraguay-s-odoo-localization-is-updated-to-v19-and-that-matters-more-than-it-sou.html
index c39157d..264c14c 100644
--- a/blog/paraguay-s-odoo-localization-is-updated-to-v19-and-that-matters-more-than-it-sou.html
+++ b/blog/paraguay-s-odoo-localization-is-updated-to-v19-and-that-matters-more-than-it-sou.html
@@ -23,8 +23,8 @@
The OC Guy
diff --git a/index.html b/index.html
index 4d7f0d3..ef58bd3 100644
--- a/index.html
+++ b/index.html
@@ -27,8 +27,8 @@
Ask a question
@@ -84,26 +84,26 @@
-
+
-
+
- O
- The latest in OCA modules
+ M
+ The latest in Modules
All OCA modules
@@ -163,15 +163,6 @@
-
-
-
-
-
Utilities
@@ -181,6 +172,8 @@
20.0
+
+
diff --git a/js/feed.js b/js/feed.js
index 11f5558..6b7defd 100644
--- a/js/feed.js
+++ b/js/feed.js
@@ -1,13 +1,12 @@
/* The OC Guy — post feed.
- One manifest (data/posts.json) written by scripts/sync_posts.py, three views:
- Tips & Tricks, Community news and the Blog. Search is client-side over
- headline + teaser + category, the same technique as blog/index.json on
- odoo4projects.com. No JS, no feed: the sections say so instead of lying. */
+ One manifest (data/posts.json) written by scripts/sync_posts.py, three views that are also
+ the three categories: Tips & Tricks, News, Modules. A post appears in exactly one of them,
+ because the category is the area. No JS, no feed: the sections say so instead of lying. */
(function () {
'use strict';
var SOURCE = 'data/posts.json';
- var BADGE = { tips: 'Tips & Tricks', news: 'Community news', blog: 'Blog' };
+ var BADGE = { tips: 'Tips & Tricks', news: 'News', modules: 'Modules' };
function el(tag, cls, text) {
var n = document.createElement(tag);
@@ -32,9 +31,11 @@
function emptyState(node, kind) {
node.textContent = '';
var box = el('p', 'feed-empty');
- box.textContent = kind === 'news'
- ? 'No community news published yet. Posts from the daily feed land here.'
- : 'Nothing published in this section yet.';
+ box.textContent = kind === 'modules'
+ ? 'No module write-ups yet. Posts in the Modules category land here.'
+ : kind === 'news'
+ ? 'No news published yet. Posts from the daily feed land here.'
+ : 'Nothing published in this section yet.';
node.appendChild(box);
}
@@ -57,31 +58,9 @@
return a;
}
- function newsRow(p) {
- var li = el('li');
- var when = el('span', 'news-date');
- var t = el('time');
- t.dateTime = p.date;
- t.textContent = fmtDate(p.date);
- when.appendChild(t);
-
- var body = el('div', 'news-body');
- var h3 = el('h3');
- var a = el('a', null, p.headline);
- a.href = p.link;
- h3.appendChild(a);
- body.appendChild(h3);
- body.appendChild(el('p', null, p.teaser));
-
- li.appendChild(when);
- li.appendChild(body);
- li.appendChild(el('span', 'news-source', p.category === 'tips' ? 'Tips & Tricks' : 'Community'));
- return li;
- }
-
function render(view, posts, term) {
var list = posts.filter(function (p) {
- return (view.all || p.category === view.category) && matches(p, term);
+ return p.category === view.category && matches(p, term);
});
var node = view.node;
if (view.countBox) {
@@ -92,17 +71,12 @@
return;
}
node.textContent = '';
- if (view.kind === 'news') {
- list.slice(0, view.limit || 5).forEach(function (p) { node.appendChild(newsRow(p)); });
- } else {
- list.slice(0, view.limit || 50).forEach(function (p) { node.appendChild(card(p)); });
- }
- if (view.count) view.count.textContent = String(list.length);
+ list.slice(0, view.limit || 50).forEach(function (p) { node.appendChild(card(p)); });
}
function pickCategory(p) {
- var c = (p.category || 'blog').toLowerCase();
- return BADGE[c] ? c : 'blog';
+ var c = (p.category || 'news').toLowerCase();
+ return BADGE[c] ? c : 'news';
}
fetch(SOURCE + '?_=' + Date.now())
@@ -117,12 +91,14 @@
});
var views = [
- { node: document.getElementById('tipsGrid'), category: 'tips', kind: 'cards',
- search: document.getElementById('tipsSearch'), count: document.getElementById('tipsCount'), limit: 3 },
- { node: document.getElementById('newsList'), category: 'news', kind: 'news',
- search: document.getElementById('newsSearch'), count: document.getElementById('newsCount'), limit: 6 },
- { node: document.getElementById('blogGrid'), category: 'blog', kind: 'cards', all: true,
- search: document.getElementById('blogSearch'), count: document.getElementById('blogCount'), limit: 60 }
+ { node: document.getElementById('tipsGrid'), category: 'tips', kind: 'tips',
+ search: document.getElementById('tipsSearch'),
+ count: document.getElementById('tipsCount'), limit: 3 },
+ { node: document.getElementById('newsGrid'), category: 'news', kind: 'news',
+ search: document.getElementById('newsSearch'),
+ count: document.getElementById('newsCount'), limit: 60 },
+ { node: document.getElementById('modulesGrid'), category: 'modules', kind: 'modules',
+ search: null, count: null, limit: 6 }
].filter(function (v) { return v.node; });
views.forEach(function (v) {
@@ -137,15 +113,13 @@
}
});
- /* posts tagged both tips and blog are listed twice on purpose:
- the sections are channels, not storage. */
var total = document.getElementById('feedTotal');
if (total) total.textContent = String(posts.length);
})
.catch(function () {
- ['tipsGrid', 'newsList', 'blogGrid'].forEach(function (id) {
+ ['tipsGrid', 'newsGrid', 'modulesGrid'].forEach(function (id) {
var n = document.getElementById(id);
- if (n) emptyState(n, id === 'newsList' ? 'news' : 'cards');
+ if (n) emptyState(n, id.replace('Grid', '').toLowerCase());
});
});
})();
diff --git a/scripts/sync_posts.py b/scripts/sync_posts.py
index acd035a..b6a4732 100644
--- a/scripts/sync_posts.py
+++ b/scripts/sync_posts.py
@@ -49,9 +49,13 @@ CATEGORY_KEYS = ("category", "area", "tags", "tag", "section", "type")
DATE_KEYS = ("scheduled", "date", "published", "pubDate", "created", "timestamp")
ID_KEYS = ("id", "slug", "uuid", "_id")
+# The three categories are the three areas of the homepage. Nothing else exists:
+# tips -> /#tips news -> /#news modules -> /#modules
KNOWN_CATEGORIES = {"tips": "tips", "tip": "tips", "tricks": "tips",
- "news": "news", "community": "news", "oca": "news",
- "blog": "blog", "article": "blog", "post": "blog"}
+ "news": "news", "community": "news", "blog": "news",
+ "article": "news", "post": "news", "release": "news",
+ "modules": "modules", "module": "modules", "oca": "modules",
+ "featured": "modules", "integration": "modules"}
def log(msg: str) -> None:
@@ -127,7 +131,7 @@ def normalise_category(value: object) -> str:
for tag in raw:
if tag in KNOWN_CATEGORIES:
return KNOWN_CATEGORIES[tag]
- return "blog"
+ return "news"
def image_urls(value: object) -> list[str]:
@@ -221,8 +225,8 @@ POST_PAGE = """
The OC Guy