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..3cc7bb7 --- /dev/null +++ b/core_directory/context_processors.py @@ -0,0 +1,19 @@ +""" +Context processor for SEO-related settings. + +Provides the INDEXABLE setting to Django templates for use in meta tags and robots.txt. +""" + +from django.conf import settings + +def seo_settings(request): + """ + Add the INDEXABLE setting to the template context. + + Args: + request: The current HttpRequest object. + + Returns: + dict: A dictionary with the INDEXABLE setting. + """ + 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..1481ec3 --- /dev/null +++ b/core_directory/sitemaps.py @@ -0,0 +1,68 @@ +""" +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 django.urls import reverse +from .models import CorePackage, Vendor + +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().order_by('sanitized_name', 'pk') + +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().order_by('vlnv_name') + +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/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/img/og-default.jpg b/core_directory/static/img/og-default.jpg new file mode 100644 index 0000000..0fe47b3 Binary files /dev/null and b/core_directory/static/img/og-default.jpg differ 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/base.html b/core_directory/templates/web_ui/base.html index 3828ec1..863df9f 100644 --- a/core_directory/templates/web_ui/base.html +++ b/core_directory/templates/web_ui/base.html @@ -3,11 +3,19 @@ - {% 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..19c088a 100644 --- a/core_directory/templates/web_ui/core_detail.html +++ b/core_directory/templates/web_ui/core_detail.html @@ -1,135 +1,196 @@ {% 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 %}
{% 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_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/core_publish.html b/core_directory/templates/web_ui/core_publish.html index a6c863e..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 %} + +