This repository hosts the source code for the official QGIS Project website:
๐ https://qgis.orgHere you'll find everything you need to build, develop, and contribute to the QGIS website.
This repository is only for the main QGIS website (qgis.org).
If you are looking for the source code or want to contribute to QGIS subdomain websites, please visit their respective repositories below.
Each subdomain has its own codebase and contribution process:
- plugins.qgis.org (GitHub: QGIS-Plugins-Website) โ QGIS Plugins Repository
- hub.qgis.org (GitHub: QGIS-Hub-Website) โ QGIS Resources Hub
- feed.qgis.org (GitHub: qgis-feed) โ QGIS Feed Manager
- planet.qgis.org (GitHub: QGIS-Planet-Website) โ QGIS Planet Blog Aggregator
- members.qgis.org (GitHub: QGIS-Members-Website) โ QGIS Sustaining Members Portal
- certification.qgis.org (GitHub: QGIS-Certification-Website) โ QGIS Certification Programme Platform
- changelog.qgis.org (GitHub: QGIS-Changelog-Website) โ QGIS Changelog Manager
- conference.qgis.org (GitHub: QGIS-UC-Website) โ QGIS User Conference Website
Table of Contents
- Internationalization and translation workflow: see I18N.md
- Development and production build guide (including incremental per-language builds and media dedup): see DEVELOPMENT.md
This project is licensed under the MIT License. See the LICENSE file for details.
QGIS-Website/
โโโ ๐ผ๏ธ assets/ # Mainly used to store the schedule.csv file
โโโ โ๏ธ config/ # Hugo configuration files
โโโ ๐ content/ # Markdown content files (pages, posts)
โโโ ๐๏ธ data/ # Data files (YAML, JSON, TOML) for site variables
โโโ ๐ docs/ # Project documentation
โโโ ๐ผ๏ธ img/ # Images files used by this README
โโโ ๐งฉ layouts/ # Hugo templates and partials
โโโ ๐งช playwright/ # Playwright end-to-end test scripts
โโโ ๐ฆ public/ # Generated site output (after `hugo` build)
โโโ ๐๏ธ resources/ # Hugo-generated resources (e.g., minified assets)
โโโ ๐ ๏ธ scripts/ # Utility scripts for development/maintenance/harvesting
โโโ ๐ป source/ # Mainly used to store the qugsneews.atom file
โโโ ๐ static/ # Static files served as-is (e.g., favicon, robots.txt)
โโโ ๐จ themes/ # Hugo themes
โโโ โ๏ธ config.toml # Main Hugo configuration file
โโโ ๐ค CONTRIBUTING.md # Contribution guidelines
โโโ ๐ fetch_feeds.py* # Script to get sustaining members and other feeds to update our web site
โโโ ๐ LICENSE # Project license
โโโ โ๏ธ Makefile # Build/Deployment automation commands
โโโ ๐ README.md # Project overview and instructions
โโโ ๐ REQUIREMENTS.txt # Python dependencies (pip)
โโโ ๐ shell.nix # Nix shell environment definition
โโโ ๐ก vscode.sh* # VSCode helper script for Nix shell environment
We are fine with using LLM's and Generative Machine Learning to act as general assistants, but the following three guidelines should be followed:
- Repeatability: Although we understand that repeatability is not possible generally, whenever you are verbatim using LLM or Generative Machine Learning outputs in this project, you must also provide the prompt that you used to generate the resource.
- Declaration: Sharing the prompt above is implicit declaration that a machine learning assistant was used. If it is not obvious that a piece of work was generated, include the robot (๐ค) icon next to a code snippet or text snippet.
- Validation: Outputs generated by a virtual assistant should always be validated by a human and you, as contributor, take ultimate responsibility for the correct functionality of any code and the correct expression in any text or media you submit to this project.
The scripts/ folder contains utility scripts to assist with data loading, and project maintenance. Below is a summary of each script:
| Script Name | Description | Affected Website Parts |
|---|---|---|
fetch_feeds.py |
Simple script to get sustaining members and other feeds and update our web site with them | ๐ข Sustaining Members page (content/funders/), ๐ฐ News feeds (data/feed.json), ๐จ Flickr gallery (content/flickr-images/) |
vscode.sh |
Launch VSCode with all settings and extensions needed to productively work on this project | ๐ ๏ธ Development environment only |
scripts/get_commit_hash.sh |
Get the current commit hash of this repository and write it in config/commit.toml for the website version display |
๐ Footer version info across all pages |
scripts/changelog_harvest.py |
Will create/update visual-changelogs at content/project/visual-changelogs based on the data at data/conf.json |
๐ Visual Changelogs pages (/project/visual-changelogs/) |
scripts/hub_images_harvest.py |
Harvest maps/screenshots images from https://hub.qgis.org and update showcase content | ๐บ๏ธ Maps and Screenshots showcase section (content/hub-maps/ and content/hub-screenshots/) |
scripts/resize_image.py |
Contains utilities to optimize images (resize, transform to webp, check validity) | ๐ผ๏ธ Images fetched automatically |
scripts/sanitize_commercial_supports.py |
Checks each supporter's URL and removes invalid ones from commercial support listings | ๐ช Commercial Support page (/resources/support/commercial-support/) |
scripts/update_donors_from_file.py |
Updates data/donors.json ordered alphabetically by first name based on input file |
๐ฐ Donors page (/funding/donate/) |
scripts/update_donors.py |
Updates data/donors.json ordered alphabetically by first name based on Stripe donations |
๐ฐ Donors page (/funding/donate/) |
scripts/update-schedule.py |
Updates data/conf.json and content/schedule.ics with release schedule information |
๐ Download page, Release schedule, LTR/LT version info |
scripts/update_individual_contributors.py |
Aggregates GitHub stats for individual contributors across QGIS repositories | ๐ฅ Individual Contributors section (/community/organisation/contributors/) |
scripts/update_contributing_orgs.py |
Aggregates GitHub stats for contributing organizations across QGIS repositories | ๐ข Contributing Organizations section (/community/organisation/organisations/) |
scripts/match_commercial_contributors.py |
Matches commercial support organizations with contributing organizations based on domain/name | ๐ช Commercial Support badges, ๐ข Contributing Organizations links |
| GitHub Actions (Automated) | Scheduled workflows for content updates | Multiple areas |
.github/workflows/update-gh-sponsors.yml |
Updates GitHub Sponsors list twice daily | ๐ GitHub Sponsors page (content/funding/donate/github-sponsors.md) |
.github/workflows/update-donors.yml |
Updates Stripe donors list twice daily | ๐ฐ Donors page (data/donors.json) |
.github/workflows/update-feeds.yml |
Updates feeds and hub maps twice daily | ๐ข Funders, ๐ฐ News feeds, ๐บ๏ธ Hub maps |
.github/workflows/check-commercial-support-links.yml |
Sanitizes commercial support links weekly | ๐ช Commercial Support page |
.github/workflows/update-commercial-matches.yml |
Updates commercial support contributor matches daily | ๐ช Commercial Support badges (data/commercial_support/contributor_matches.json) |
โ๏ธ Note: Run each script from the project root. Some scripts may require environment variables or configurationโsee comments within each script for usage details.
Please refer to the Nix section in CONTRIBUTING.md.
We welcome contributions! Please read the CONTRIBUTING.md for guidelines on how to get started.
Have questions or feedback? Feel free to open an issue or submit a Pull Request!
- Tim Sutton โ Original author and lead maintainer of the QGIS Website project
- Kontur Team โ Responsible for the design and development of the current website version
- Lova Andriarimalala โ Core developer and ongoing maintainer
- QGIS Contributors โ See the full list of amazing contributors who have helped make this website possible.
- Hugo Language and Syntax Support
- Color Highlight
Clone the repo:
git clone https://github.com/qgis/QGIS-Website.git
Run the site:
Press Ctl-Shift-D then choose the following runner:
'Run dev using locally installed Hugo'
the click the green triangle next to the runner to start it.
Once the site is running, you can open it at:
The site will automatically refresh any page you have open if you edit it and save your work. Magical eh?
Use an appropriate Hugo plugin for your IDE, or run Hugo frรฅn the command line:
hugo serverYou can then visit the hot-reloaded site in your browser at http://localhost:1313/
Test files are located in playwright/ci-test/tests.
These tests exist to make sure code changes to this repository do not break how the site currently functions. They are intended to run on each commit to verify the site is working in the expected order.
Requirements: NodeJS v18+
- Install playwright: If you haven't already installed Playwright, you can do so by running the following commands in
playwright/ci-testdirectory.
cd playwright/ci-test
npm install- Install playwright browsers:
npx playwright install --with-deps chromium- Install the extension Playwright Test for VSCode: This extension provides a seamless integration of Playwright testing into VSCode.
-
Open the Testing Tab: In VSCode, click on the Testing icon in the Activity Bar on the side of the window. This will open the Testing tab.
-
Run Playwright Tests from the Testing Tab:
- In the Testing tab, you should see your Playwright tests listed. If not, ensure the browser is checked under Playwright > Project menu.
- Click on the refresh button in the Testing tab to reload the tests.
- You can run individual tests by clicking the play button next to the test name.
- You can also run all tests by clicking the play button at the top of the Testing tab.
- Debugging Tests:
- You can debug individual tests by clicking the debug icon (a small bug with a play button) next to the test name.
- Make sure to set breakpoints in your test files before running the debugger.
By default, this will run in headless mode just as it is in CI.
./run-tests.shNOTE: To run it in UI mode, add the --ui tag to the script.
$PLAYWRIGHT \
test \
--ui \
--project chromiumRead more on testing here.
There is a github action that will run the tests automatically on PR submission, merge.
See .github/workflows/playwright-e2e.yml
You can harvest data from various feeds using the fetch_feeds.py script. By default flickr harvesting is disabled in this script so run it manually and review the content to see if it is suitable for publication on our site.
./fetch_feeds.py --flickr=yesFlickr parsing creates new files and md pages with param draft: true. It can be changed to false after manual verification. The script will not overwrite the changes. Pictures with draft: false will appear on /project/overview/maps/ and /project/overview/screenshots/.
This script is run nightly as a github action (see .github/workflows/update-feeds.yml).
The search functionality uses both FuseJS and MarkJS.
The search functionality code is based on this Blog Post and GitHub Gist by Eddie Webb.
Content folders need to be excluded from search, by making them headless bundles - which we have done for the sustaining member and flagship user folders in content/. To make other content folders which are not rendered and included in search results, add an index.md file with the following content: headless = true.
The site needs to work in production, where the links of the site are all below the root URL, and in staging, where the site is deployed to GitHub pages in a subpath. To ensure both deployment strategies work, please use the following method of constructing URLs in templates.
<a class="button is-primary" href="{{ "donate/" | absURL }}">Note: We do not use a leading slash, only an ending slash.
SASS for most components is stored in themes/qgis-website-theme/assets/sass/bulma/components/
Some common styles are places in themes/qgis-website-theme/assets/sass/style.sass - this file is compiled as hugo template, hence has access to config.toml variables and hugo macroses
Also some bulma theme overrides are placed in themes/qgis-website-theme/assets/css/custom.css
- Separate words in file names with hyphens e.g. windows-download.md
- Avoid abbreviations in the words of your files
- Write file names in lower case only
- No spaces in file names
We welcome your contributions! All contributors are expected to sign a contributor license agreement (CLA) which you can see here. This process is automatically enabled when you create your first pull request via https://cla-assistant.io/.
| Page type | Path |
|---|---|
| Landing Page | themes/qgis/layouts/index.html |
| Top Level Pages | themes/qgis/layouts/_default/single.html |
The layout of the landing page is themes/qgis-website-theme/layouts/index.html: the main page has many diverse blocks, that are not used anywhere else, hence its content is mostly in the partials.
The content/_index.md contains the front matter of the page and the contents for the feature shortcodes. Just edit whatever you like there. The blocks shortcodes are described here
Content pages are stored in the content folder. The top level documents there will be rendered with the top level page theming.
For example to add an about page, create content/about.md
The page will be accessible then at /about/
Place images and media in static/img. Everything in static is referenced
from the top level of the site e.g. static/img/foo.png would be referenced in
markdown as /img/foo.png.
The site uses a number of shortcodes to create reusable blocks of content. These are defined in the themes/qgis-website-theme/layouts/shortcodes/ folder.
The shortcodes with screenshots are described here
TODO
Sidebar is implemented in themes/qgis-website-theme/layouts/partials/sidebar.html
Items are retrieved from config.toml under [menu] section. weight parameter defines the order of the item.
To enable sidebar on the content page, use the following template:
---
type: "page"
...
sidebar: true
---
{{< content-start >}}
... add content here ...
{{< content-end >}}
Script for parsing release schedule from the google spreadsheet is scripts/update-schedule.py. It creates data/conf.json with variables that can be used later in md/html.
Script for harvesting the visualchangelog from changelog.qgis.org is scripts/changelog_harvest.py. It will create/update a visual-changelogs at content/project/visual-changelogs based on the data at data/conf.json.
Usage:
$ ./scripts/changelog_harvest.py
# For a specific version
$ ./scripts/changelog_harvest.py --version 3.34 --release 21.06.2024
# Resizing the images for existing changelog
$ ./scripts/changelog_harvest.py --version 3.34 --release 21.06.2024 --use_existing
Example usage in md:
Here you will simply install the latest stable QGIS ({{< param "version" >}}.x {{< param "codename" >}})
To include param as link, use param-link shortcode: it correctly renders as md link, and not as plain text
Electronic document: {{< param-link "userguidecite" >}}
Example usage in HTML:
to use params inside shortcode or partial, use wrapper:
{{ with index .Site.Data.conf }}
...
<a
class="button is-primary1 mb-3"
href="{{ .ltr_msi }}"
onclick="thanks(this)"
download
>Long Term Version for Windows ({{ .ltrversion }} {{ .ltrnote }})</a>
{{ end }}
Example usage as shortcode param:
tricky part: shortcodes can't be used as other shortcodes' params. So you need to make replacement in the outer shortcode: "tabs.html" make a replacement of |ltrversion| and |version| to the values from config.
{{< tabs tab1="QGIS |ltrversion|" tab2="QGIS |version|" tab3="QGIS testing (>|ltrversion|)" tab4="Archived releases">}}
This table represents required redirects from old URL structure to the new one: https://docs.google.com/spreadsheets/d/12Oo761Zwgj4iLeJxdrg8I31rGzsB95z5M1PpW9pYma8/edit?usp=sharing
Notes:
- it will be more convenient to store all redirects in same file (nginx conf or else)
- hugo aliases not recommended: they work, but don't support regex. also hard to maintain: redirects are spread across content files
- use regexps to redirect from all langs
- use regexps to map between similar stuff (case studies, visual changelog) - example rewrite rules below
Bulk redirects can be done like
map $url $new_url {
...
^/[A-Za-z-]+/site/about/case_studies/(.*).html /project/case-studies/$1/;
^/[A-Za-z-]+/site/forusers/visualchangelog(.*)/index.html /project/visual-changelogs/visualchangelog$1/;
^/[A-Za-z-]+/site/forusers/usergroups.html /community/organisation/groups/;
...
}
See nginx map tutorial and examples with regex capture
main donation page: https://qgis.github.io/QGIS-Website/funding/donate/
Stripe widget can be included to any page with the shortcode {{<stripe-widget>}}
Payment options are implemented with Payment Links. Values and links can be updated in data/stripe_products.yml
Note: payment links also support recurring donations (subscriptions) - products with recurrent payments should be created for that matter
The Donors list stored in data/donors.json can be updated from one or multiple
static txt file using the script script/update_donors_from_file.py:
python scripts/update_donors_from_file.py <path_to_donors.txt> <path_to_donors2.txt> ...Is not ported yet. Donors are stored in data/donors.json. adddonor.pl and related scripts & webhooks should be adapted to the new format
docker run --rm dcycle/broken-link-checker:3 https://qgis.github.io/QGIS-Website > broken_links.csv
Crawls the site and reports all 404. Full run takes apout 10 mins
Made with โค๏ธ by Tim Sutton (@timlinux), Lova Andriarimalala (@Xpirix) and QGIS Contributors.




