diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index 1816520402..02c52f102b 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -72,6 +72,8 @@ jobs: continue-on-error: false privileged: ${{ fromJSON(needs.metadata.outputs.privileged) }} strict: true + secrets: + RO_DISCOURSE_API_KEY: ${{ secrets.RO_DISCOURSE_API_KEY }} build_i18n: if: ${{ contains(github.event.pull_request.labels.*.name, 'ci:build i18n') }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3d562fe8fd..7b40b46bc6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,6 +30,9 @@ on: cache: type: boolean default: true + secrets: + RO_DISCOURSE_API_KEY: + required: false permissions: contents: read @@ -172,23 +175,31 @@ jobs: sudo apt install pngquant echo "EXTRA_FLAGS=""$EXTRA_FLAGS" --cmd=mkdocs"" >> "$GITHUB_ENV" + - name: Generate Donating Members List + continue-on-error: true + env: + DISCOURSE_API_KEY: ${{ secrets.RO_DISCOURSE_API_KEY }} + run: | + pip install requests + python generate-members.py > includes/members.md + - name: Build Website run: | eval ./run.sh --build --lang=${{ inputs.lang }} "$EXTRA_FLAGS" - - name: Run index-generation.sh for top posts + - name: Run generate-topics.sh for top posts if: inputs.lang == 'en' run: | - bash index-generation.sh \ + bash generate-topics.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 + - name: Run generate-topics.sh for latest posts if: inputs.lang == 'en' run: | - bash index-generation.sh \ + bash generate-topics.sh \ --source='https://discuss.privacyguides.net/latest.json' \ --tag="latest posts" \ --destination="./site/en/index.html" \ @@ -259,6 +270,14 @@ jobs: path: site-${{ inputs.config }}-${{ inputs.lang }}.tar.gz retention-days: 1 + - name: Upload members list + uses: actions/upload-artifact@v4 + if: inputs.lang == 'en' + with: + name: members.md + path: includes/members.md + retention-days: 1 + offline_package: if: inputs.config == 'offline' && inputs.lang == 'en' needs: build diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 1d56da6e04..066ac2b118 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -63,6 +63,8 @@ jobs: context: production continue-on-error: false cache: false + secrets: + RO_DISCOURSE_API_KEY: ${{ secrets.RO_DISCOURSE_API_KEY }} build_blog: needs: submodule diff --git a/.github/workflows/update-discussions.yml b/.github/workflows/update-discussions.yml index 1d57e08a7a..3cfb99fae4 100644 --- a/.github/workflows/update-discussions.yml +++ b/.github/workflows/update-discussions.yml @@ -55,17 +55,17 @@ jobs: source: /en/index.html target: ./site/en/ - - name: Run index-generation.sh for top posts + - name: Run generate-topics.sh for top posts run: | - bash index-generation.sh \ + bash generate-topics.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 + - name: Run generate-topics.sh for latest posts run: | - bash index-generation.sh \ + bash generate-topics.sh \ --source='https://discuss.privacyguides.net/latest.json' \ --tag="latest posts" \ --destination="./site/en/index.html" \ diff --git a/.gitignore b/.gitignore index 9fdbda0238..7cf59f16de 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,6 @@ node_modules .venv .env .mkdocs-insiders-* + +# Automatically generated +includes/members.md diff --git a/docs/about/donate.md b/docs/about/donate.md index fa8cfc2f56..30bd2728bc 100644 --- a/docs/about/donate.md +++ b/docs/about/donate.md @@ -3,57 +3,117 @@ title: Donate description: The charitable mission of Privacy Guides relies on contributions from visitors like yourself. Anything you can do to support the project is hugely appreciated. --- -Donate to Privacy Guides and support our mission to defend digital rights and spread the word about mass surveillance programs and other daily privacy invasions. You can help Privacy Guides researchers, activists, and maintainers create informative content, host private digital services, and protect privacy rights at a time when the world needs it most. +Support our mission to defend digital rights and spread the word about mass surveillance programs and other daily privacy invasions. You can help Privacy Guides researchers, activists, and maintainers create informative content, host private digital services, and protect privacy rights at a time when the world needs it most. -Privacy Guides has been a nonstop effort for over 5 years to stay up to date with the world of cybersecurity and privacy, and to promote the benefits of privacy overall. This is a **non-profit, community-driven** project that would not be possible without the generous support of all our [contributors](contributors.md). +[:material-heart:{ .pg-red } Become a Member](https://donate.magicgrants.org/privacyguides/membership){ class="md-button md-button--primary" } +[:material-hand-coin: Make a Donation](https://donate.magicgrants.org/privacyguides/donate/privacyguides){ class="md-button md-button--primary" } -## Donate + -MAGIC Grants is our fiscal host, and their custom, open-source donation platform allows you to donate to our project with **Monero**, **Bitcoin**, or **debit/credit card**. +MAGIC Grants is our fiscal host, and their custom, open-source donation platform allows you to donate to our project with **Monero**, **Bitcoin**, or **debit/credit card**. You can also donate using [:simple-github: GitHub Sponsors](https://github.com/sponsors/privacyguides). -[:material-heart:{ .pg-red } Donate](https://donate.magicgrants.org/privacyguides){ class="md-button md-button--primary" } -[Sponsor on GitHub](https://github.com/sponsors/privacyguides){ class="md-button" } + -Donating with Monero will maximize your donation by lowering our transaction fees while simultaneously [preserving your privacy](../cryptocurrency.md), win-win! You can also donate to us via GitHub Sponsors if you prefer, or if you would like to publicize your support. GitHub does not charge us any fees if you donate as an individual, but may charge us fees if you donate with a GitHub organization, if this is a concern for you. +## Foundations & Organizations -## How We Use Donations +=== "Current Supporters" -Privacy Guides is a **non-profit** project. Your donation will go to a [dedicated fund](https://magicgrants.org/funds/privacy_guides) within [MAGIC Grants](https://magicgrants.org), a 501(c)(3) organization and our fiscal host. The funds will **only** be used for this project specifically. + Thank you to these organizations who significantly support Privacy Guides. (1) + { .annotate } -You may qualify for a tax deduction. When you donate to us [here](https://donate.magicgrants.org/privacyguides) with cryptocurrency or card you have the option to receive a receipt from MAGIC Grants for this purpose. If you have questions about other transactions please email . + 1. Please contact to inquire about giving. Privacy Guides reserves the right to rescind the membership of those who are unaligned with our mission or organization at any time. Organizational members have no ability to influence what content is recommended on the Privacy Guides website. Learn more about our [donation acceptance policy](donation-acceptance-policy.md). -We use donations for a variety of purposes, including: +
-**Web Hosting** + [![Power Up Privacy]](https://powerupprivacy.com){ rel=nofollow target=_blank title="Power Up Privacy" } -: Traffic to this website uses hundreds of gigabytes of data per month, we use a variety of service providers to keep up with this traffic. + [Power Up Privacy]: ../assets/img/donors/power-up-privacy.webp -**Payroll** +
-: We are endeavoring to [hire](jobs.md) full-time journalists and writers to review products and create more educational content on a regular basis. +=== "Past Supporters" -**Domain Registrations** + Thank you to these organizations who have substantially supported our project in the past. -: We have a few domain names like `privacyguides.org` which cost us around $10 yearly to maintain their registration. + - [Safing](https://safing.io){ rel=nofollow target=_blank }: 2019 – 2021 -**Online Services** +## Active Members -: We host [internet services](services.md) for testing and showcasing different privacy-products we like and [recommend](../tools.md). Some of them are made publicly available for our community's use (SearXNG, Tor, etc.), and some are provided for our team members (email, etc.). +Privacy Guides would not be possible without these individuals who generously donate on a monthly or yearly basis. (1) +{ .annotate } -**Product Purchases** +1. If you [become a member](https://donate.magicgrants.org/privacyguides/membership) and [link your donation](https://discuss.privacyguides.net/t/getting-your-member-flair-on-the-forum/25453) to your forum account, you're automatically added here with a link to your profile and avatar to show your support for Privacy Guides. If you link your forum account but don't set your flair or title, you'll be a silent +1. You can change your visibility any time. This chart is updated upon each website release. -: We occasionally purchase products and services for the purposes of testing our [recommended tools](../tools.md). +
+
-Thank you to all those who support our mission! :material-heart:{ .pg-red } +--8<-- "includes/members.md" + +
+
+ + + +This is a list of our **active** [members](https://donate.magicgrants.org/privacyguides/membership), plus donors on GitHub, who have chosen to make their donation public. Hundreds more have donated in the past or privately, and their support is hugely appreciated as well. -We strictly **cannot** use donations to support political campaigns/candidates or attempt to influence legislation. Earnings also will **not** inure to the benefit of any private shareholder or individual. + ## Merchandise -Another option to support us is by buying our merchandise from HelloTux. We get a small commission for each item sold, and you get a quality product to show for it. +You can support us and share your passion for privacy by buying our merchandise from HelloTux. [Buy on HelloTux.com](https://hellotux.com/privacyguides){ class="md-button" } ## Non-Financial Support It takes a lot of [people](contributors.md) and [work](https://github.com/privacyguides/privacyguides.org/pulse/monthly) to keep Privacy Guides up to date and spread the word about privacy and mass surveillance. If you're looking for other ways to help out, consider getting involved by [editing the site](https://github.com/privacyguides/privacyguides.org), [joining our forum](https://discuss.privacyguides.net), or [contributing translations](https://crowdin.com/project/privacyguides). + +## FAQ + +### What is an organizational membership? + +Organizational membership to Privacy Guides is open to any company, private foundation, or organization that donates at least $5,000 per year. While Privacy Guides does not endorse private companies or their products, we're grateful for their contributions. Your donation may be tax-deductible, and we will provide you with a receipt. + +You can become an organizational member by reaching out to for more information. + +### How are organizational members recognized? + +Organizational members that choose to be recognized publicly are included in our organizational members section (above), and occasionally at other opportunities where appropriate. Organizational member links include the `rel="nofollow"` attribute: We adopted this policy to screen out potential abuse of our program and site to raise the rank of third parties in search algorithms. Unfortunately, this is a growing problem for nonprofits. This was a complex decision since we know many of the sincere supporters behind these companies, but we decided that it was the best choice for us. + +Organizational members have no ability to influence what content is recommended on the Privacy Guides website. Learn more about our [donation acceptance policy](donation-acceptance-policy.md). + +### What is an active membership? + +Your monthly or yearly membership sustains Privacy Guides's services and public activism for privacy and cybersecurity year round. If you become a member, we will recognize your support here on our website, our community forum, and occasionally in other areas like our videos if you choose to make your membership publicly known. + +Our membership program is brand new, and we are still exploring other ways that we can share a token of our appreciation with you, while maintaining sustainable and ethical boundaries. Stay tuned! + +### How does Privacy Guides use donations? + +Privacy Guides has been a nonstop effort for over 5 years to stay up to date with the world of cybersecurity and privacy, and to promote the benefits of privacy overall. This is a **non-profit, community-driven** project that would not be possible without the generous support of all our [contributors](contributors.md), in addition to our regularly donating members above. + +Your donation go to a [dedicated fund](https://magicgrants.org/funds/privacy_guides) within [MAGIC Grants](https://magicgrants.org), a 501(c)(3) organization and our fiscal host. The funds will **only** be used for this project specifically. + +You may qualify for a tax deduction. When you donate to us [here](https://donate.magicgrants.org/privacyguides) with cryptocurrency or card you have the option to receive a receipt from MAGIC Grants for this purpose. If you have questions about other transactions please email . + +We use donations for a variety of purposes, including: + +**Payroll** + +: We have journalists, writers, and video creators on payroll to review products and create more educational content on a regular basis. This is a significant expense, and we are only able to create our quantity of content with your support. + +**Web Hosting and Infrastructure** + +: Traffic to this website uses hundreds of gigabytes of data per month, we use a variety of service providers to keep up with this traffic. + +**Online Services** + +: We host [internet services](services.md) for testing and showcasing different privacy-products we like and [recommend](../tools.md). Some of them are made publicly available for our community's use (SearXNG, Tor, etc.), and some are provided for our team members (email, etc.). + +**Product Purchases** + +: We occasionally purchase products and services for the purposes of testing our [recommended tools](../tools.md). + +Thank you to all those who support our mission! :material-heart:{ .pg-red } + +We strictly **do not** use donations to support political campaigns/candidates or attempt to influence legislation. Earnings will **not** inure to the benefit of any private shareholder or individual. diff --git a/generate-members.py b/generate-members.py new file mode 100644 index 0000000000..4b4669dfc4 --- /dev/null +++ b/generate-members.py @@ -0,0 +1,93 @@ +import requests +import os + +GITHUB_API_URL = "https://api.github.com/graphql" +GITHUB_TOKEN = os.getenv("GH_TOKEN") +ORG_NAME = "privacyguides" + +# Fetch members from the API +members_api_url = "https://discuss.privacyguides.net/g/members/members.json?offset=0&order=added_at&asc=true" +headers = { + "Api-Key": os.getenv("DISCOURSE_API_KEY"), + "Api-Username": "system" +} +members_response = requests.get(members_api_url, headers=headers) +members_data = members_response.json() + +if 'members' not in members_data: + raise KeyError("Response JSON does not contain 'members' key") + +members = members_data['members'] +private_members_count = 0 + +html_output = "" +for member in members: + flair_name = member.get('flair_name') + title = member.get('title') + if flair_name == "members" or title == "Member": + username = member['username'] + avatar_template = member['avatar_template'] + avatar_url = f"https://discuss.privacyguides.net{avatar_template.replace('{size}', '128')}" + profile_url = f"https://discuss.privacyguides.net/u/{username}" + html_output += f'' + else: + private_members_count += 1 + +# print(html_output) + +query = """ +{ + organization(login: "%s") { + sponsorshipsAsMaintainer(first: 100) { + nodes { + sponsorEntity { + ... on User { + login + avatarUrl + url + } + ... on Organization { + login + avatarUrl + url + } + } + createdAt + } + } + } +} +""" % ORG_NAME + +headers = { + "Authorization": f"Bearer {GITHUB_TOKEN}", + "Content-Type": "application/json" +} + +response = requests.post(GITHUB_API_URL, json={'query': query}, headers=headers) +data = response.json() + +if 'errors' in data: + raise Exception(f"GraphQL query failed with errors: {data['errors']}") +if 'data' not in data: + raise KeyError(f"Response JSON does not contain 'data' key: {data}") + +sponsors = data['data']['organization']['sponsorshipsAsMaintainer']['nodes'] + +# Sort sponsors by the date they began their sponsorship +sponsors.sort(key=lambda x: x['createdAt']) + +for sponsor in sponsors: + sponsor_entity = sponsor['sponsorEntity'] + login = sponsor_entity['login'] + avatar_url = sponsor_entity['avatarUrl'] + url = sponsor_entity['url'] + html_output += f'' + +private_members_count += 6 + +# Append the count of private members +if private_members_count > 0: + html_output += f'+{private_members_count}' + +print(html_output) diff --git a/index-generation.sh b/generate-topics.sh similarity index 100% rename from index-generation.sh rename to generate-topics.sh diff --git a/mkdocs.yml b/mkdocs.yml index f6a4e0aed1..99d81daddf 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -287,11 +287,11 @@ theme: - search.highlight extra_css: - - assets/stylesheets/extra.css?v=20240802 + - assets/stylesheets/extra.css?v=20250306 extra_javascript: - - path: assets/javascripts/randomize-element.js?v=20240801 + - path: assets/javascripts/randomize-element.js?v=20250306 defer: true - - path: assets/javascripts/feedback.js?v=20240801 + - path: assets/javascripts/feedback.js?v=20250306 defer: true watch: @@ -450,6 +450,8 @@ nav: - "device-integrity.md" - !ENV [NAV_BLOG, "Articles"]: "/articles/" - !ENV [NAV_VIDEOS, "Videos"]: /videos/ + - !ENV [NAV_FORUM, "Forum"]: + !ENV [NAV_FORUM_LINK, "https://discuss.privacyguides.net/"] - !ENV [NAV_ABOUT, "About"]: - "about.md" - "about/donate.md" @@ -477,9 +479,6 @@ nav: - "meta/uploading-images.md" - "meta/git-recommendations.md" - "meta/commit-messages.md" - - !ENV [NAV_DONATE, "Donate"]: https://donate.magicgrants.org/privacyguides - - !ENV [NAV_FORUM, "Forum"]: - !ENV [NAV_FORUM_LINK, "https://discuss.privacyguides.net/"] validation: nav: diff --git a/theme/assets/img/donors/power-up-privacy.webp b/theme/assets/img/donors/power-up-privacy.webp new file mode 100644 index 0000000000..97c0ceb432 Binary files /dev/null and b/theme/assets/img/donors/power-up-privacy.webp differ diff --git a/theme/assets/stylesheets/extra.css b/theme/assets/stylesheets/extra.css index 0bfef2cc1d..2faf2e0023 100644 --- a/theme/assets/stylesheets/extra.css +++ b/theme/assets/stylesheets/extra.css @@ -527,3 +527,75 @@ path[d="M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l- list-style-type: disc; padding-inline-start: 1em; } + +/* Donations */ +.md-typeset .mdx-specialthanks p { + margin:2em 0; + text-align:center +} +.md-typeset .mdx-specialthanks img { + height:3.25rem +} +.md-typeset .mdx-specialthanks p:last-child { + display:flex; + flex-wrap:wrap; + justify-content:center +} +.md-typeset .mdx-specialthanks p:last-child>a { + display:block; + flex-shrink:0 +} +.md-typeset .mdx-donors__list { + margin:2em 0 +} +.md-typeset .mdx-donors__list:after { + clear:both; + content:""; + display:block +} +[dir=ltr] .md-typeset .mdx-donors__item { + float:left +} +[dir=rtl] .md-typeset .mdx-donors__item { + float:right +} +.md-typeset .mdx-donors__item { + border-radius:100%; + display:block; + height:3rem; + margin:.2rem; + overflow:hidden; + transform:scale(1); + transition:color 125ms,transform 125ms; + width:3rem +} +.md-typeset .mdx-donors__item:focus, +.md-typeset .mdx-donors__item:hover { + transform:scale(1.1) +} +.md-typeset .mdx-donors__item:focus img, +.md-typeset .mdx-donors__item:hover img { + filter:grayscale(0) +} +.md-typeset .mdx-donors__item--private { + background:var(--md-default-fg-color--lightest); + color:var(--md-default-fg-color--lighter); + font-size:.8rem; + font-weight:700; + line-height:2.9rem; + text-align:center +} +.md-typeset .mdx-donors__item img { + display:block; + filter:grayscale(100%) opacity(75%); + height:auto; + transition:filter 125ms; + width:100% +} +.md-typeset .mdx-donors-button { + font-weight:400 +} +.md-typeset .mdx-donors-count, +.md-typeset .mdx-donors-total { + font-weight:700 +} diff --git a/theme/main.html b/theme/main.html index 36d2ba1aaf..f3090eee18 100644 --- a/theme/main.html +++ b/theme/main.html @@ -92,23 +92,3 @@ {% endif %} {% endblock %} - -{% if config.theme.language == "en" and config.extra.context == "production" %} - {% block announce %} - - We don't run ads, we don't use affiliate links, and we don't have paywalls. We rely on our readers to build this community and spread the word.
- If you've received $3 worth of knowledge here, please donate today if you're able to. It really helps. - - - {% include ".icons/material/heart.svg" %} - - - Donate now - - - {% include ".icons/material/arrow-right.svg" %} - - -
- {% endblock %} -{% endif %} diff --git a/theme/partials/donate.html b/theme/partials/donate.html new file mode 100644 index 0000000000..45548fc8ed --- /dev/null +++ b/theme/partials/donate.html @@ -0,0 +1,8 @@ +
+
+ {% set icon = "material/heart" %} + + {% include ".icons/" ~ icon ~ ".svg" %} + +
+
diff --git a/theme/partials/header.html b/theme/partials/header.html index b4516c3bc6..e2da3d2d15 100644 --- a/theme/partials/header.html +++ b/theme/partials/header.html @@ -136,6 +136,8 @@ {% if not config.theme.palette is mapping %} {% include "partials/javascripts/palette.html" %} {% endif %} + + {% include "partials/donate.html" %}