From 7124691367e598a11f4e0c1bc58fd83f1712071e Mon Sep 17 00:00:00 2001 From: anlu Date: Fri, 26 Sep 2025 12:26:11 +0200 Subject: [PATCH 1/5] Adds SEO enhancements and sitemap generation Implements SEO enhancements by adding robots.txt and meta robots tag based on a setting to control search engine indexing. Also generates dynamic sitemaps for vendors, core packages, and static views, improving site discoverability. The search engine indexing setting is controlled via the `SEARCH_ENGINE_INDEXING` environment variable. --- .env.example | 5 ++ core_directory/context_processors.py | 4 ++ core_directory/models.py | 18 +++++ core_directory/sitemaps.py | 66 +++++++++++++++++++ core_directory/templates/web_ui/base.html | 1 + .../web_ui/includes/meta_robots.html | 4 ++ core_directory/templates/web_ui/landing.html | 1 + core_directory/views/__init__.py | 1 + core_directory/views/system_views.py | 14 ++++ project/settings.py | 6 ++ project/urls.py | 12 ++++ 11 files changed, 132 insertions(+) create mode 100644 core_directory/context_processors.py create mode 100644 core_directory/sitemaps.py create mode 100644 core_directory/templates/web_ui/includes/meta_robots.html create mode 100644 core_directory/views/system_views.py diff --git a/.env.example b/.env.example index c38c734..5205e7a 100644 --- a/.env.example +++ b/.env.example @@ -28,3 +28,8 @@ GITHUB_REPO= # A GitHub personal access token with the required permissions for your app. # Never share or commit your real token! GITHUB_ACCESS_TOKEN= + +# Should search engines (Google, Bing, etc.) be allowed to index this site? +# Set to 'True' to allow indexing (site will be discoverable in search engines). +# Set to 'False' to prevent indexing (site will not be indexed by search engines). +SEARCH_ENGINE_INDEXING=False \ No newline at end of file diff --git a/core_directory/context_processors.py b/core_directory/context_processors.py new file mode 100644 index 0000000..0f3ade8 --- /dev/null +++ b/core_directory/context_processors.py @@ -0,0 +1,4 @@ +from django.conf import settings + +def seo_settings(request): + return {'INDEXABLE': settings.INDEXABLE} diff --git a/core_directory/models.py b/core_directory/models.py index f7d94ed..4889bb5 100644 --- a/core_directory/models.py +++ b/core_directory/models.py @@ -4,6 +4,7 @@ from django.core.exceptions import ValidationError from django.db import models +from django.urls import reverse from semver import VersionInfo from utils.sanitize import get_unique_sanitized_name from utils.spdx import get_spdx_choices, get_spdx_license_url, validate_spdx @@ -38,6 +39,12 @@ def __str__(self): """Return the vendors's name as its string representation.""" return f'{self.name}' + def get_absolute_url(self): + """ + Returns the canonical URL for this vendor. + """ + return reverse('vendor-detail', kwargs={'sanitized_name': self.sanitized_name}) + class Library(UniqueSanitizedNameMixin): """Represents a library for a vendor.""" @@ -166,6 +173,17 @@ def get_license_url(self): return get_spdx_license_url(self.spdx_license) return None + def get_absolute_url(self): + """ + Returns the canonical URL for this core package version. + """ + return reverse('core-detail-vlnv', kwargs={ + 'vendor': self.project.vendor.sanitized_name, + 'library': self.project.library.sanitized_name or '~', + 'core': self.project.sanitized_name, + 'version': self.sanitized_name, + }) + def clean(self): """ Validates that the version string is a valid semantic version with all components. diff --git a/core_directory/sitemaps.py b/core_directory/sitemaps.py new file mode 100644 index 0000000..76c263b --- /dev/null +++ b/core_directory/sitemaps.py @@ -0,0 +1,66 @@ +""" +This module defines sitemap classes for the Django application. +Classes: + ProjectSitemap: Generates sitemap entries for all Project objects. + CorePackageSitemap: Generates sitemap entries for all CorePackage objects. + StaticViewSitemap: Generates sitemap entries for static views such as 'landing', 'core-package-list', and 'vendor-list'. +Each sitemap class specifies the change frequency and priority for its entries, and provides methods to retrieve the items to be included in the sitemap. +""" + +from django.contrib.sitemaps import Sitemap +from .models import CorePackage, Vendor +from django.urls import reverse + +class VendorSitemap(Sitemap): + """ + Sitemap class for listing all Vendor objects. + Attributes: + changefreq (str): The frequency at which the vendor pages are likely to change. + priority (float): The priority of vendor pages in the sitemap. + Methods: + items(): Returns a queryset of all Vendor objects to be included in the sitemap. + """ + changefreq = "daily" + priority = 0.6 + + def items(self): + return Vendor.objects.all() + +class CorePackageSitemap(Sitemap): + """ + Sitemap class for CorePackage objects. + + This class defines the sitemap configuration for CorePackage entries, + specifying how frequently the sitemap should be updated and the priority + of these entries for search engines. + + Attributes: + changefreq (str): Suggested frequency of changes for CorePackage objects ("daily"). + priority (float): Priority of CorePackage objects in the sitemap (0.8). + + Methods: + items(): Returns a queryset of all CorePackage objects to be included in the sitemap. + """ + changefreq = "daily" + priority = 0.8 + def items(self): + return CorePackage.objects.all() + +class StaticViewSitemap(Sitemap): + """ + Sitemap for static views in the application. + + Attributes: + priority (float): The priority of the sitemap entries. + changefreq (str): How frequently the pages are likely to change. + + Methods: + items(): Returns a list of static view names to include in the sitemap. + location(item): Returns the URL for a given static view name. + """ + priority = 0.5 + changefreq = "weekly" + def items(self): + return ['landing', 'core-package-list', 'vendor-list'] + def location(self, item): + return reverse(item) diff --git a/core_directory/templates/web_ui/base.html b/core_directory/templates/web_ui/base.html index 3828ec1..691c91e 100644 --- a/core_directory/templates/web_ui/base.html +++ b/core_directory/templates/web_ui/base.html @@ -8,6 +8,7 @@ + {% include "web_ui/includes/meta_robots.html" %} diff --git a/core_directory/templates/web_ui/includes/meta_robots.html b/core_directory/templates/web_ui/includes/meta_robots.html new file mode 100644 index 0000000..0de1470 --- /dev/null +++ b/core_directory/templates/web_ui/includes/meta_robots.html @@ -0,0 +1,4 @@ +{# include/meta_robots.html #} +{% if not INDEXABLE %} + +{% endif %} \ No newline at end of file diff --git a/core_directory/templates/web_ui/landing.html b/core_directory/templates/web_ui/landing.html index bc6ef30..75cb6e1 100644 --- a/core_directory/templates/web_ui/landing.html +++ b/core_directory/templates/web_ui/landing.html @@ -5,6 +5,7 @@ FuseSoC Package Directory + {% include "web_ui/includes/meta_robots.html" %} diff --git a/core_directory/views/__init__.py b/core_directory/views/__init__.py index 5b33068..e415e6e 100644 --- a/core_directory/views/__init__.py +++ b/core_directory/views/__init__.py @@ -1,4 +1,5 @@ """Exposes all views for the app by importing API and web views.""" from .api_views import * +from .system_views import * from .web_views import * diff --git a/core_directory/views/system_views.py b/core_directory/views/system_views.py new file mode 100644 index 0000000..e80ac0c --- /dev/null +++ b/core_directory/views/system_views.py @@ -0,0 +1,14 @@ +"""System views for core_directory""" + +from django.http import HttpResponse +from django.conf import settings + +def robots_txt(request): + """ + Serve the robots.txt file based on the site's indexability setting. + """ + if settings.INDEXABLE: + content = "User-agent: *\nDisallow:" + else: + content = "User-agent: *\nDisallow: /" + return HttpResponse(content, content_type="text/plain") diff --git a/project/settings.py b/project/settings.py index 20992bc..b385af1 100644 --- a/project/settings.py +++ b/project/settings.py @@ -34,6 +34,9 @@ if not SECRET_KEY: raise ValueError("No DJANGO_SECRET_KEY set for Django application") +# Option to control search engine indexing via environment variable +INDEXABLE = os.environ.get('SEARCH_ENGINE_INDEXING', 'False').lower() in ('true', '1', 'yes') + # SECURITY WARNING: don't run with debug turned on in production! DEBUG = os.environ.get('DJANGO_DEBUG', 'False').lower() in ('true', '1', 'yes') @@ -72,12 +75,14 @@ SECURE_CONTENT_TYPE_NOSNIFF = True X_FRAME_OPTIONS = 'DENY' + # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', + 'django.contrib.sitemaps', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', @@ -108,6 +113,7 @@ 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', + 'core_directory.context_processors.seo_settings', ], }, }, diff --git a/project/urls.py b/project/urls.py index 5f7cd4a..c2c26fe 100644 --- a/project/urls.py +++ b/project/urls.py @@ -19,8 +19,11 @@ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin +from django.contrib.sitemaps.views import sitemap from django.urls import include, path +from core_directory.sitemaps import CorePackageSitemap, StaticViewSitemap, VendorSitemap +from core_directory.views.system_views import robots_txt from core_directory.views.web_views import ( landing, core_detail, @@ -31,6 +34,12 @@ vendor_detail ) +sitemaps = { + 'corepackages': CorePackageSitemap, + 'static': StaticViewSitemap, + 'vendors': VendorSitemap, +} + urlpatterns = [ path('', landing, name='landing'), @@ -53,4 +62,7 @@ path('admin/', admin.site.urls), path('api/', include('core_directory.urls')), + + path('robots.txt', robots_txt, name='robots_txt'), + path('sitemap.xml', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'), ] From 0ba2852f9b3bc31657e95e4ec122cbde9819e49b Mon Sep 17 00:00:00 2001 From: anlu Date: Fri, 26 Sep 2025 21:44:43 +0200 Subject: [PATCH 2/5] Improves SEO and site appearance in social media Enhances SEO by adding meta descriptions, og tags, and structured data (JSON-LD) to various templates. This allows for richer previews when the site is shared on social media platforms and improves search engine visibility. Also includes a default og-image and canonical URLs. --- core_directory/static/img/og-default.jpg | Bin 0 -> 66742 bytes core_directory/templates/web_ui/base.html | 11 +++- .../templates/web_ui/core_detail.html | 52 +++++++++++++++++- .../templates/web_ui/core_packages_list.html | 45 ++++++++++++++- core_directory/templates/web_ui/landing.html | 28 ++++++++++ .../templates/web_ui/vendor_detail.html | 39 ++++++++++++- .../templates/web_ui/vendor_list.html | 44 ++++++++++++++- 7 files changed, 213 insertions(+), 6 deletions(-) create mode 100644 core_directory/static/img/og-default.jpg diff --git a/core_directory/static/img/og-default.jpg b/core_directory/static/img/og-default.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0fe47b36add82aa1438d2896558cf31309505dc9 GIT binary patch literal 66742 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 p4PvvSw~=j91MbrtJc{;&>N~M7$3A(CHRzg3jFT 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 - {% block title %}FuseSoC Package Directory{% endblock %} - {% block title_suffix %}{% endblock %} + {% block title %}FuseSoC-PD{% endblock %} | FuseSoC Package Directory + + + + + + + + {% include "web_ui/includes/meta_robots.html" %} diff --git a/core_directory/templates/web_ui/core_detail.html b/core_directory/templates/web_ui/core_detail.html index b78ce35..9874db4 100644 --- a/core_directory/templates/web_ui/core_detail.html +++ b/core_directory/templates/web_ui/core_detail.html @@ -1,8 +1,58 @@ {% extends "web_ui/base.html" %} {% load static %} -{% block title_suffix %}| {{ core }}{% endblock %} +{% block title %}{{ core }}{% endblock %} +{% block meta_description %}{{ core.description|default:core.project.description }}{% endblock %} +{% block og_title %} View {{ core }} on FuseSoC Package Directory{% endblock %} +{% block og_description %}{{ core.description|default:core.project.description }}{% endblock %} {% block extra_head %} + + {% endblock %} {% block content %}
diff --git a/core_directory/templates/web_ui/core_packages_list.html b/core_directory/templates/web_ui/core_packages_list.html index d6c5917..3c4dde1 100644 --- a/core_directory/templates/web_ui/core_packages_list.html +++ b/core_directory/templates/web_ui/core_packages_list.html @@ -1,5 +1,48 @@ {% extends "web_ui/base.html" %} -{% block title_suffix %}| Cores{% endblock %} +{% block title %}Cores{% endblock %} +{% block meta_description %}Browse all core packages in the FuseSoC Package Directory.{% endblock %} +{% block og_title %}Core Packages on FuseSoC Package Directory{% endblock %} +{% block og_description %}Browse all core packages in the FuseSoC Package Directory.{% endblock %} +{% block extra_head %} + + +{% endblock %} {% block content %}
{% include "web_ui/includes/card_header.html" with icon="bi bi-list" title="Core Packages" %} diff --git a/core_directory/templates/web_ui/landing.html b/core_directory/templates/web_ui/landing.html index 75cb6e1..21396d5 100644 --- a/core_directory/templates/web_ui/landing.html +++ b/core_directory/templates/web_ui/landing.html @@ -3,9 +3,37 @@ FuseSoC Package Directory + + + + + + + {% include "web_ui/includes/meta_robots.html" %} + + + diff --git a/core_directory/templates/web_ui/vendor_detail.html b/core_directory/templates/web_ui/vendor_detail.html index a92ffb4..41161f2 100644 --- a/core_directory/templates/web_ui/vendor_detail.html +++ b/core_directory/templates/web_ui/vendor_detail.html @@ -1,8 +1,45 @@ {% extends "web_ui/base.html" %} {% load static %} -{% block title_suffix %}| {{ vendor }}{% endblock %} +{% block title %}{{ vendor }}{% endblock %} +{% block meta_description %}Cores and libraries provided by {{ vendor }} on FuseSoC Package Directory.{% endblock %} +{% block og_title %}Explore cores and libraries from {{ vendor }} on FuseSoC Package Directory{% endblock %} +{% block og_description %}Cores and libraries provided by {{ vendor }} on FuseSoC Package Directory.{% endblock %} {% block extra_head %} + + {% endblock %} {% block content %}
diff --git a/core_directory/templates/web_ui/vendor_list.html b/core_directory/templates/web_ui/vendor_list.html index 1d5421c..bee6875 100644 --- a/core_directory/templates/web_ui/vendor_list.html +++ b/core_directory/templates/web_ui/vendor_list.html @@ -1,5 +1,47 @@ {% extends "web_ui/base.html" %} -{% block title_suffix %}| Vendors{% endblock %} +{% block title %}Vendors{% endblock %} +{% block meta_description %}Browse all vendors providing open source hardware cores in the FuseSoC Package Directory.{% endblock %} +{% block og_title %}Browse all vendors on FuseSoC Package Directory{% endblock %} +{% block og_description %}Browse all vendors providing open source hardware cores in the FuseSoC Package Directory.{% endblock %} +{% block extra_head %} + + +{% endblock %} {% block content %}
{% include "web_ui/includes/card_header.html" with icon="bi bi-list" title="Vendors" %} From ad388a6c789cc487902a612ef240375f966c608d Mon Sep 17 00:00:00 2001 From: anlu Date: Mon, 29 Sep 2025 17:28:11 +0200 Subject: [PATCH 3/5] Improves UI with expandable sections Refactors the UI to use expandable sections for better organization and user experience. This change introduces a new JavaScript file to handle the expanding/collapsing behavior of sections, and updates the CSS to style the expandable sections. It converts several static cards to use collapsible components, enhancing readability and navigation, and also improves keyboard accessibility. Adds sitemap generation based on settings configuration. --- core_directory/static/css/common.css | 4 +- .../static/js/expandable_section.js | 52 +++- .../static/js/publish_core_from_github.js | 102 ++++---- .../templates/web_ui/core_detail.html | 243 +++++++++--------- .../templates/web_ui/core_publish.html | 76 +++--- .../web_ui/includes/card_header.html | 4 +- core_directory/templates/web_ui/landing.html | 4 +- .../templates/web_ui/vendor_detail.html | 66 ++--- project/urls.py | 9 +- 9 files changed, 311 insertions(+), 249 deletions(-) diff --git a/core_directory/static/css/common.css b/core_directory/static/css/common.css index a7ff681..0eca0cd 100644 --- a/core_directory/static/css/common.css +++ b/core_directory/static/css/common.css @@ -21,7 +21,6 @@ margin-bottom: 1.5rem; } .section-header { - cursor: pointer; background: #f8f9fa; font-weight: 500; border-radius: 0.5rem 0.5rem 0 0; @@ -30,6 +29,9 @@ padding: 0.75rem 1rem; font-size: 1.1rem; } +.section-card.collapsible > .section-header { + cursor: pointer; +} .section-content { display: none; background: #fff; diff --git a/core_directory/static/js/expandable_section.js b/core_directory/static/js/expandable_section.js index aab6c65..75428bd 100644 --- a/core_directory/static/js/expandable_section.js +++ b/core_directory/static/js/expandable_section.js @@ -1,18 +1,42 @@ document.addEventListener('DOMContentLoaded', function() { - document.querySelectorAll('.section-header').forEach(function(header) { - header.addEventListener('click', function() { - var content = header.nextElementSibling; - var btn = header.querySelector('.expand-toggle'); - // Toggle visibility - if (content.style.display === 'none' || content.style.display === '') { - content.style.display = 'block'; - // Optionally animate: - content.style.maxHeight = content.scrollHeight + "px"; - btn.innerHTML = ''; - } else { - content.style.display = 'none'; - btn.innerHTML = ''; + document.querySelectorAll('.section-card.collapsible').forEach(function(card) { + var header = card.querySelector('.section-header'); + var content = card.querySelector('.section-content'); + var toggle = header.querySelector('.expand-toggle'); + var collapsed = card.getAttribute('data-collapsed') === 'true'; + + // Set initial state + if (collapsed) { + content.style.display = 'none'; + if (toggle) toggle.innerHTML = ''; + } else { + content.style.display = 'block'; + if (toggle) toggle.innerHTML = ''; + } + + // Toggle function + function doToggle() { + var isOpen = content.style.display === 'block'; + content.style.display = isOpen ? 'none' : 'block'; + if (toggle) { + toggle.innerHTML = isOpen + ? '' + : ''; } + } + + // Click handler on header (but ignore if button was clicked) + header.addEventListener('click', function(e) { + if (e.target.closest('.expand-toggle')) return; // Don't toggle twice if button clicked + doToggle(); }); + + // Click handler on button (for keyboard accessibility) + if (toggle) { + toggle.addEventListener('click', function(e) { + e.stopPropagation(); // Prevent header handler from firing + doToggle(); + }); + } }); -}); +}); \ No newline at end of file diff --git a/core_directory/static/js/publish_core_from_github.js b/core_directory/static/js/publish_core_from_github.js index a458004..c6a48ed 100644 --- a/core_directory/static/js/publish_core_from_github.js +++ b/core_directory/static/js/publish_core_from_github.js @@ -61,60 +61,66 @@ document.addEventListener('DOMContentLoaded', function() { let html = ''; if (obj.repo) { html += ` -
- ${getCardHeaderHtml("bi-github", "Repository details")} -
-
- ${obj.repo.name} - @${obj.parsed_version} -
-

${obj.repo.description || ''}

-

- ⭐ ${obj.repo.stars} - 🍴 ${obj.repo.forks} -

-
+
+
+ + Repository details +
+
+

+ ${obj.repo.name} + @${obj.parsed_version} +

+

${obj.repo.description || ''}

+

+ ⭐ ${obj.repo.stars} + 🍴 ${obj.repo.forks} +

+
`; } if (obj.core_files) { html += ` -
- ${getCardHeaderHtml("bi-boxes", "Select core")} -
`; - if (obj.core_files.length > 0) { - const defaultCore = obj.core_files[0].core; - const defaultHasSig = obj.core_files[0].hasSig; - html += ` -
- - - +
+
+ + Select core +
+
`; + if (obj.core_files.length > 0) { + const defaultCore = obj.core_files[0].core; + const defaultHasSig = obj.core_files[0].hasSig; + html += ` +
+ -
- `; - } else { - html += - `
- No .core files found in the repository.
- - Note: Only .core files located in the root of the repository can be published. - -
`; - } + + +
+
+ `; + } else { + html += + `
+ No .core files found in the repository.
+ + Note: Only .core files located in the root of the repository can be published. + +
`; + } html += `
`; } resultDiv.innerHTML = html; diff --git a/core_directory/templates/web_ui/core_detail.html b/core_directory/templates/web_ui/core_detail.html index 9874db4..19c088a 100644 --- a/core_directory/templates/web_ui/core_detail.html +++ b/core_directory/templates/web_ui/core_detail.html @@ -58,128 +58,139 @@
{% include "web_ui/includes/card_header.html" with icon="bi bi-box" title=core %}
- -
- Description: -
{{ core.description }}
-
-
- {% if core.spdx_license %} - {% if core.get_license_url %} - - - License: {{ core.spdx_license }} - - {% else %} - - - License: {{ core.spdx_license }} - - {% endif %} - {% else %} - - - License unknown - - {% endif %} - {% if core.is_signed %} - - - signed - - {% else %} - - - unsigned - - {% endif %} -
-
Files
- - .core - - {% if core.is_signed %} - - .sig - - {% endif %} -
Targets
- {% for item in targets_with_deps %} - {% with target_configuration=item.target_configuration dependencies=item.dependencies %} -
-
- - - - {{ target_configuration.target }} +
+
Details
+
+ -
+ {% endwith %} + {% endfor %}
- {% endwith %} - {% endfor %} +
{% endblock %} \ No newline at end of file diff --git a/core_directory/templates/web_ui/core_publish.html b/core_directory/templates/web_ui/core_publish.html index a6c863e..8fbaf64 100644 --- a/core_directory/templates/web_ui/core_publish.html +++ b/core_directory/templates/web_ui/core_publish.html @@ -19,39 +19,46 @@
{% include "web_ui/includes/card_header.html" with icon="bi bi-cloud-plus" title="Publish new core" %}
-
-
- - - - How to publish a FuseSoC Core -
- + + +
+
+ +

How to publish a FuseSoC Core

+
+
+

+ Use this page to publish a FuseSoC core directly from a GitHub repository.
+ You can select the latest commit, a specific tag, or enter a commit hash to target a particular version. + Only .core files located in the root of the repository are supported. +

+
    +
  1. Enter a GitHub repo URL and select a version (latest, tag, or commit).
  2. +
  3. Parse to list available .core files.
  4. +
  5. Select the core you like to publish, validate it, and then publish.
  6. +
+

+ Make sure the repository you specify actually contains the local core with its source files - not just a core description file that points to another provider. + External cores already containing a provider section cannot be published here; use the API in those cases. +

+

+ Note: When publishing using this web interface, the core will automatically be converted into a remote core. A provider section will be added to the core, referencing the selected GitHub commit by its SHA. +

+

+ Core signing is not supported: This web interface does not currently support signing of published cores or publishing of existing signatures. +

-
- {% include "web_ui/includes/card_header.html" with icon="bi bi-shop" title="Select GitHub repository" %} -
+
+ + +
+

+ + Select GitHub repository +

+
@@ -79,9 +86,12 @@
+ +
+
- + {% endblock %} \ No newline at end of file diff --git a/core_directory/templates/web_ui/includes/card_header.html b/core_directory/templates/web_ui/includes/card_header.html index 2b1c360..cf5dbed 100644 --- a/core_directory/templates/web_ui/includes/card_header.html +++ b/core_directory/templates/web_ui/includes/card_header.html @@ -1,7 +1,7 @@ {# includes/card_header.html #}
-

+

{{ title }} -

+
\ No newline at end of file diff --git a/core_directory/templates/web_ui/landing.html b/core_directory/templates/web_ui/landing.html index 21396d5..d85d5f1 100644 --- a/core_directory/templates/web_ui/landing.html +++ b/core_directory/templates/web_ui/landing.html @@ -44,9 +44,9 @@
-
+

FuseSoC Package Directory -

+
Discover, search, and reuse open source hardware cores for your next project.
diff --git a/core_directory/templates/web_ui/vendor_detail.html b/core_directory/templates/web_ui/vendor_detail.html index 41161f2..6a1e702 100644 --- a/core_directory/templates/web_ui/vendor_detail.html +++ b/core_directory/templates/web_ui/vendor_detail.html @@ -45,40 +45,44 @@
{% include "web_ui/includes/card_header.html" with icon="bi bi-shop" title=vendor %}
-
Libraries
- {% for library in libraries %} -
-
- - - - {{ library }} -
-
+ {% endfor %}
- {% endfor %} +
{% endblock %} \ No newline at end of file diff --git a/project/urls.py b/project/urls.py index c2c26fe..a9d21d5 100644 --- a/project/urls.py +++ b/project/urls.py @@ -18,6 +18,7 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ +from django.conf import settings from django.contrib import admin from django.contrib.sitemaps.views import sitemap from django.urls import include, path @@ -63,6 +64,10 @@ path('admin/', admin.site.urls), path('api/', include('core_directory.urls')), - path('robots.txt', robots_txt, name='robots_txt'), - path('sitemap.xml', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'), + path('robots.txt', robots_txt, name='robots_txt') ] + +if settings.INDEXABLE: + urlpatterns += [ + path('sitemap.xml', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'), + ] From 6745ca05389409c3cce7981878224b0e534ddd2c Mon Sep 17 00:00:00 2001 From: anlu Date: Mon, 29 Sep 2025 17:45:44 +0200 Subject: [PATCH 4/5] Improves SEO for the core publish page. Adds metadata, schema.org markup, and breadcrumbs to enhance search engine optimization for the "Publish core" page. This change makes the page more discoverable and provides richer information to search engines about the page's content and context within the FuseSoC Package Directory. --- .../templates/web_ui/core_publish.html | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/core_directory/templates/web_ui/core_publish.html b/core_directory/templates/web_ui/core_publish.html index 8fbaf64..70296ac 100644 --- a/core_directory/templates/web_ui/core_publish.html +++ b/core_directory/templates/web_ui/core_publish.html @@ -1,7 +1,44 @@ {% extends "web_ui/base.html" %} {% load static %} -{% block title_suffix %}| Publish core{% endblock %} +{% block title %}Publish core{% endblock %} +{% block meta_description %}Publish a FuseSoC core directly from a GitHub repository. Select a version, validate, and publish your open source hardware core.{% endblock %} +{% block og_title %}Publish core | FuseSoC Package Directory{% endblock %} +{% block og_description %}Publish a FuseSoC core directly from a GitHub repository. Select a version, validate, and publish your open source hardware core.{% endblock %} {% block extra_head %} + +