From 455f18be13a944051be13d6e75441b8691d3db8f Mon Sep 17 00:00:00 2001 From: Jonah Aragon Date: Fri, 20 Sep 2024 02:34:13 -0500 Subject: [PATCH] feat: Generate homepage topics on server side (#2766) --- .github/workflows/build.yml | 18 ++ .github/workflows/update-discussions.yml | 82 ++++++++ index-generation.sh | 89 +++++++++ mkdocs.net.yml | 197 +++++++++++++++++++ net/index.md | 42 ++++ theme/assets/javascripts/discourse-topics.js | 170 ---------------- theme/home.html | 22 +-- 7 files changed, 436 insertions(+), 184 deletions(-) create mode 100644 .github/workflows/update-discussions.yml create mode 100755 index-generation.sh create mode 100644 mkdocs.net.yml create mode 100644 net/index.md delete mode 100644 theme/assets/javascripts/discourse-topics.js diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 26edc9ac..6d99dc1b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -176,6 +176,24 @@ jobs: run: | eval ./run.sh --build --lang=${{ inputs.lang }} "$EXTRA_FLAGS" + - name: Run index-generation.sh for top posts + if: inputs.lang == 'en' + run: | + bash index-generation.sh \ + --source='https://discuss.privacyguides.net/top.json?period=weekly' \ + --tag="top posts" \ + --destination="./site/en/index.html" \ + --count=3 + + - name: Run index-generation.sh for latest posts + if: inputs.lang == 'en' + run: | + bash index-generation.sh \ + --source='https://discuss.privacyguides.net/latest.json' \ + --tag="latest posts" \ + --destination="./site/en/index.html" \ + --count=12 + - name: Package Website run: | tar -czf site-${{ inputs.config }}-${{ inputs.lang }}.tar.gz site diff --git a/.github/workflows/update-discussions.yml b/.github/workflows/update-discussions.yml new file mode 100644 index 00000000..226d714e --- /dev/null +++ b/.github/workflows/update-discussions.yml @@ -0,0 +1,82 @@ +# Copyright (c) 2024 Jonah Aragon + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +name: 🔄 Update Discussions + +on: + workflow_dispatch: + schedule: + - cron: "*/30 * * * *" + +permissions: + contents: read + +jobs: + generate: + runs-on: ubuntu-latest + permissions: + contents: read + environment: + name: production + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + persist-credentials: "false" + fetch-depth: 1 + + - name: Create site/en directory + run: mkdir -p site/en + + - name: Update Discussions + uses: yakubique/minio-download@v1.1.1 + with: + endpoint: https://${{ vars.PROD_GARAGE_HOSTNAME }} + access_key: ${{ secrets.PROD_GARAGE_KEY_ID }} + secret_key: ${{ secrets.PROD_GARAGE_SECRET_KEY }} + bucket: ${{ vars.PROD_GARAGE_BUCKET }} + source: /en/index.html + target: ./site/en/ + + - name: Run index-generation.sh for top posts + run: | + bash index-generation.sh \ + --source='https://discuss.privacyguides.net/top.json?period=weekly' \ + --tag="top posts" \ + --destination="./site/en/index.html" \ + --count=3 + + - name: Run index-generation.sh for latest posts + run: | + bash index-generation.sh \ + --source='https://discuss.privacyguides.net/latest.json' \ + --tag="latest posts" \ + --destination="./site/en/index.html" \ + --count=12 + + - name: Upload modified index + uses: yakubique/minio-upload@v1.1.3 + with: + endpoint: https://${{ vars.PROD_GARAGE_HOSTNAME }} + access_key: ${{ secrets.PROD_GARAGE_KEY_ID }} + secret_key: ${{ secrets.PROD_GARAGE_SECRET_KEY }} + bucket: ${{ vars.PROD_GARAGE_BUCKET }} + source: ./site/en/index.html + target: /en diff --git a/index-generation.sh b/index-generation.sh new file mode 100755 index 00000000..89acc744 --- /dev/null +++ b/index-generation.sh @@ -0,0 +1,89 @@ +#!/bin/bash + +DATE_CMD="date" + +# Check if the script is running on macOS +if [[ "$OSTYPE" == "darwin"* ]]; then + DATE_CMD="gdate" +fi + +# Defaults +source="https://discuss.privacyguides.net/top.json?period=weekly" +tag="top posts" +destination="./site/en/index.html" +count=3 + +for arg in "$@" +do + case $arg in + --source=*) + source="${arg#*=}" + shift + ;; + --tag=*) + tag="${arg#*=}" + shift + ;; + --destination=*) + destination="${arg#*=}" + shift + ;; + --count=*) + count="${arg#*=}" + shift + ;; + esac +done + +# URL of the Discourse top.json +DISCOURSE_URL="$source" + +# Fetch the JSON data +json_data=$(curl -s $DISCOURSE_URL) + +# Extract the first 3 topics +topics=$(echo $json_data | jq -r ".topic_list.topics[:$count]") + +users=$(echo $json_data | jq -r ".users") +# Generate HTML for the first 3 posts +html_output="" +for row in $(echo "${topics}" | jq -r '.[] | @base64'); do + _jq() { + echo ${row} | base64 --decode | jq -r ${1} + } + + title=$(_jq '.title') + id=$(_jq '.id') + like_count=$(_jq '.like_count') + reply_count=$(_jq '.posts_count') + views=$(_jq '.views') + + author_id=$(_jq '.posters[0].user_id') + author_info=$(echo "${users}" | jq -r ".[] | select(.id==$author_id)") + author_username=$(echo "${author_info}" | jq -r ".username") + + html_output+="
  • " + html_output+="

    ${title}

    " + html_output+="
    " + html_output+="

    " + html_output+="" + html_output+="" + html_output+=" Posted by $author_username" + html_output+="" + html_output+="

    " + html_output+="

    " + html_output+="eye" + html_output+=" ${views} " + html_output+="heart" + html_output+=" ${like_count} " + html_output+="reply" + html_output+=" ${reply_count} " + html_output+="

    " + html_output+="
  • " +done + +tempfile=$(mktemp) +echo "$html_output" > $tempfile + +# Insert the HTML output between the comments in index.html +sed -i'.bak' "//,//{//!d;}; //r $tempfile" $destination diff --git a/mkdocs.net.yml b/mkdocs.net.yml new file mode 100644 index 00000000..ee7ccf75 --- /dev/null +++ b/mkdocs.net.yml @@ -0,0 +1,197 @@ +# Copyright (c) 2022-2024 Jonah Aragon + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +docs_dir: "net" +site_url: "https://www.privacyguides.net/" +site_dir: "site/net" + +site_name: Privacy Guides Community +site_description: "Discover privacy tools and resources, ask questions, and stay informed at the biggest digital rights and privacy tech community online." +edit_uri_template: blob/main/net/{path}?plain=1 + +extra: + privacy_guides: + footer: + intro: + !ENV [ + FOOTER_INTRO, + "Privacy Guides is a non-profit, socially motivated website that provides information for protecting your data security and privacy.", + ] + note: + !ENV [ + FOOTER_NOTE, + "We do not make money from recommending certain products, and we do not use affiliate links.", + ] + copyright: + author: + !ENV [FOOTER_COPYRIGHT_AUTHOR, "Privacy Guides and contributors."] + date: !ENV [FOOTER_COPYRIGHT_DATE, "2019-2024"] + license: + - fontawesome/brands/creative-commons + - fontawesome/brands/creative-commons-by + - fontawesome/brands/creative-commons-sa + homepage: https://www.privacyguides.org/en/ + generator: false + context: !ENV [BUILD_CONTEXT, "production"] + offline: !ENV [BUILD_OFFLINE, false] + deploy: !ENV DEPLOY_ID + social: + - icon: simple/mastodon + link: https://mastodon.neat.computer/@privacyguides + name: !ENV [SOCIAL_MASTODON, "Mastodon"] + - icon: simple/matrix + link: https://matrix.to/#/#privacyguides:matrix.org + name: !ENV [SOCIAL_MATRIX, "Matrix"] + - icon: simple/discourse + link: https://discuss.privacyguides.net/ + name: !ENV [SOCIAL_FORUM, "Forum"] + - icon: simple/github + link: https://github.com/privacyguides + name: !ENV [SOCIAL_GITHUB, "GitHub"] + - icon: simple/torbrowser + link: http://www.xoe4vn5uwdztif6goazfbmogh6wh5jc4up35bqdflu6bkdc5cas5vjqd.onion/ + name: !ENV [SOCIAL_TOR_SITE, "Hidden service"] + +repo_url: + !ENV [BUILD_REPO_URL, "https://github.com/privacyguides/privacyguides.org"] +repo_name: "" + +theme: + name: material + language: en + custom_dir: theme + font: + text: Public Sans + code: DM Mono + palette: + - media: "(prefers-color-scheme)" + scheme: default + accent: deep purple + toggle: + icon: material/brightness-auto + name: !ENV [THEME_DARK, "Switch to dark mode"] + - media: "(prefers-color-scheme: dark)" + scheme: slate + accent: amber + toggle: + icon: material/brightness-2 + name: !ENV [THEME_LIGHT, "Switch to light mode"] + - media: "(prefers-color-scheme: light)" + scheme: default + accent: deep purple + toggle: + icon: material/brightness-5 + name: !ENV [THEME_AUTO, "Switch to system theme"] + favicon: assets/brand/logos/png/favicon-32x32.png + icon: + repo: simple/github + features: + - announce.dismiss + - navigation.tracking + - navigation.tabs + - navigation.sections + - navigation.expand + - navigation.path + - navigation.indexes + - navigation.footer + - content.action.edit + - content.tabs.link + - content.tooltips + - search.highlight + +extra_css: + - assets/stylesheets/extra.css?v=20240829 + +watch: + - theme + - includes + +plugins: + tags: {} + privacy: + enabled: !ENV [BUILD_PRIVACY, true] + offline: + enabled: !ENV [BUILD_OFFLINE, false] + group: + enabled: !ENV [BUILD_INSIDERS, true] + plugins: + macros: {} + meta: {} + optimize: + enabled: !ENV [OPTIMIZE, PRODUCTION, NETLIFY, false] + typeset: {} + social: + cards: !ENV [CARDS, true] + cards_dir: assets/img/social + cards_layout_dir: theme/layouts + cards_layout: page + +markdown_extensions: + admonition: {} + pymdownx.details: {} + pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + pymdownx.tabbed: + alternate_style: true + pymdownx.arithmatex: + generic: true + pymdownx.critic: {} + pymdownx.caret: {} + pymdownx.keys: {} + pymdownx.mark: {} + pymdownx.tilde: {} + pymdownx.snippets: + auto_append: + - !ENV [BUILD_ABBREVIATIONS, "includes/abbreviations.en.txt"] + pymdownx.tasklist: + custom_checkbox: true + attr_list: {} + def_list: {} + md_in_html: {} + meta: {} + abbr: {} + pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg + tables: {} + footnotes: {} + toc: + permalink: true + toc_depth: 4 + +nav: + - !ENV [NAV_HOME, "Home"]: https://www.privacyguides.org/en/ + - !ENV [NAV_KNOWLEDGE_BASE, "Knowledge Base"]: + https://www.privacyguides.org/en/basics/why-privacy-matters/ + - !ENV [NAV_RECOMMENDATIONS, "Recommendations"]: + https://www.privacyguides.org/en/tools/ + - !ENV [NAV_BLOG, "Articles"]: https://www.privacyguides.org/articles/ + - !ENV [NAV_ABOUT, "About"]: https://www.privacyguides.org/en/about/ + - "Donate": https://www.privacyguides.org/en/about/donate/ + - !ENV [NAV_CHANGELOG, "Changelog"]: + "https://github.com/privacyguides/privacyguides.org/releases" + - !ENV [NAV_FORUM, "Forum"]: "https://discuss.privacyguides.net/" + +validation: + nav: + not_found: info diff --git a/net/index.md b/net/index.md new file mode 100644 index 00000000..24e271ab --- /dev/null +++ b/net/index.md @@ -0,0 +1,42 @@ +--- +title: Weekly Discussions +meta_title: Privacy Guides Community +hide: + - footer + - toc + - navigation +schema: + - + "@context": https://schema.org + "@type": Organization + "@id": https://www.privacyguides.org/ + name: Privacy Guides + url: https://www.privacyguides.org/ + logo: https://www.privacyguides.org/en/assets/brand/logos/png/square/pg-yellow.png + sameAs: + - https://twitter.com/privacy_guides + - https://github.com/privacyguides + - https://www.wikidata.org/wiki/Q111710163 + - https://www.youtube.com/@privacyguides + - https://mastodon.neat.computer/@privacyguides + - + "@context": https://schema.org + "@type": WebSite + name: Privacy Guides + alternateName: ["Privacy Guides Community", "Privacy Guides Forum", "Privacy & Security Forum", "Privacy Discussions", "Privacy Community", "PG Community"] + url: "https://www.privacyguides.net/" + isPartOf: "https://www.privacyguides.org/" +--- + +Discover privacy tools and resources, ask questions, and stay informed at the biggest digital rights and privacy tech community online. + +[Open Forum](https://discuss.privacyguides.net){.md-button .md-button--primary} +[View Resources](https://www.privacyguides.org/en/tools/){.md-button} + +
    +
      + + + +
    +
    diff --git a/theme/assets/javascripts/discourse-topics.js b/theme/assets/javascripts/discourse-topics.js deleted file mode 100644 index 325aaf4f..00000000 --- a/theme/assets/javascripts/discourse-topics.js +++ /dev/null @@ -1,170 +0,0 @@ -/** - * @overview Generates a list of topics on a Discourse forum. - * @author Jonah Aragon - * @version 3.1.0 - * @license - * Copyright (c) 2023 - 2024 Jonah Aragon - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -async function getData(url) { - const response = await fetch(url); - return response.json() -} - -async function main() { - const elements = document.querySelectorAll("ul[data-forum]"); - - for (let j = 0; j < elements.length; j++) { - - var topics = elements[j]; - var dataset = topics.dataset; - - console.log("Fetching data from " + dataset.feed) - const data = await getData(dataset.feed); - var list = data['topic_list']['topics']; - var profiles = data['users']; - var count = dataset.count; - - for (var i = 0; i < count; i++) { - - if (list[i]['pinned'] == true) { - count++; - continue; - } - - var title = list[i]['title']; - var id = list[i]['id']; - - var topic = document.createElement("li"); - topic.className = "discourse-topic"; - - var h3 = document.createElement('p'); - h3.className = "discourse-title"; - var a1 = document.createElement('a'); - - a1.href = dataset.forum + '/t/' + id; - - var boldTitle = document.createElement('strong'); - boldTitle.innerText = title; - a1.appendChild(boldTitle); - h3.appendChild(a1); - - var authorinfo = document.createElement('p'); - authorinfo.className = "discourse-author"; - - var author_id = list[i]['posters'][0]['user_id']; - var author_data = profiles.find(profile => profile['id'] == author_id); - var author = document.createElement('span'); - author.className = "discourse-author"; - var avatar = document.createElement('img'); - avatar.src = dataset.forum + author_data['avatar_template'].replace("{size}", "40"); - avatar.width = 20; - avatar.height = 20; - avatar.className = "middle"; - avatar.loading = "lazy"; - avatar.ariaHidden = "true"; - avatar.alt = ""; - author.appendChild(avatar); - var namespan = document.createElement('span'); - namespan.innerText = " Posted by " + author_data['username']; - author.appendChild(namespan); - authorinfo.appendChild(author); - - var postinfo = document.createElement('p'); - postinfo.className = "discourse-data"; - - var dateIcon = document.createElement('span'); - dateIcon.className = "twemoji"; - dateIcon.innerHTML = ''; - - var date = document.createElement('span'); - date.className = "discourse-date"; - var datestring = list[i]['bumped_at']; - var dateobject = new Date(datestring); - var now = new Date(); - var diff = now - dateobject; - var minutes = Math.floor(diff / 60000); - var hours = Math.floor(minutes / 60); - var days = Math.floor(hours / 24); - if (days > 0) { - if (days == 1) { - date.innerText = " 1 day ago "; - } - else { - date.innerText = " " + days + " days ago "; - } - } - else if (hours > 0){ - if (hours == 1) { - date.innerText = " 1 hour ago "; - } - else { - date.innerText = " " + hours + " hours ago "; - } - } - else { - if (minutes == 1) { - date.innerText = " 1 minute ago "; - } - else { - date.innerText = " " + minutes + " minutes ago "; - } - } - postinfo.appendChild(dateIcon); - postinfo.appendChild(date); - - var likesicon = document.createElement('span'); - likesicon.classList = "twemoji pg-red"; - likesicon.innerHTML = ''; - - var likes = document.createElement('span'); - likes.className = "discourse-likes"; - likes.innerText = " " + list[i]['like_count'] + " "; - postinfo.appendChild(likesicon); - postinfo.appendChild(likes); - - var replyIcon = document.createElement('span'); - replyIcon.classList = "twemoji"; - replyIcon.innerHTML = ''; - - var replies = document.createElement('span'); - replies.className = "discourse-replies"; - - var reply_count = list[i]['posts_count'] - 1; - if (reply_count == 1) { - replies.innerText = "1 Reply" - } - else { - replies.innerText = " " + reply_count - } - postinfo.appendChild(replyIcon); - postinfo.appendChild(replies); - - topic.appendChild(h3); - topic.appendChild(document.createElement('hr')); - topic.appendChild(authorinfo); - topic.appendChild(postinfo); - topics.appendChild(topic); - } - } -} - -main(); diff --git a/theme/home.html b/theme/home.html index 49315419..3fa6a186 100644 --- a/theme/home.html +++ b/theme/home.html @@ -52,11 +52,10 @@ {% if config.theme.language == "en" %}

    Top discussions this week

    -
      +
        + + +
    @@ -103,11 +102,10 @@

    Latest discussions

    -
      +
        + + +
    {% endif %} {% endblock %} -{% block scripts %} - - {{ super() }} -{% endblock %}