341 lines
12 KiB
Python
341 lines
12 KiB
Python
"""
|
|
SEO Module für Kasico Fursuit Shop
|
|
Meta Tags, Structured Data, Sitemap Generation
|
|
"""
|
|
|
|
from django.utils.html import strip_tags
|
|
from django.template.loader import render_to_string
|
|
from django.contrib.sites.models import Site
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
import json
|
|
|
|
|
|
class SEOManager:
|
|
"""SEO Manager für Meta Tags und Structured Data"""
|
|
|
|
def __init__(self, request=None):
|
|
self.request = request
|
|
self.site = Site.objects.get_current()
|
|
self.base_url = f"https://{self.site.domain}"
|
|
|
|
def get_product_meta_tags(self, product):
|
|
"""Meta Tags für Produkt-Seiten"""
|
|
meta_tags = {
|
|
'title': f"{product.name} - Kasico Fursuit Shop",
|
|
'description': self._clean_description(product.description),
|
|
'keywords': f"fursuit, {product.fursuit_type}, {product.style}, custom fursuit, furry",
|
|
'og_title': product.name,
|
|
'og_description': self._clean_description(product.description),
|
|
'og_type': 'product',
|
|
'og_image': self._get_product_image_url(product),
|
|
'og_url': f"{self.base_url}{product.get_absolute_url()}",
|
|
'twitter_card': 'summary_large_image',
|
|
'twitter_title': product.name,
|
|
'twitter_description': self._clean_description(product.description),
|
|
'twitter_image': self._get_product_image_url(product),
|
|
'canonical_url': f"{self.base_url}{product.get_absolute_url()}",
|
|
}
|
|
|
|
# Preis-Informationen für E-Commerce
|
|
if product.on_sale:
|
|
meta_tags['price'] = str(product.sale_price)
|
|
meta_tags['original_price'] = str(product.base_price)
|
|
else:
|
|
meta_tags['price'] = str(product.base_price)
|
|
|
|
return meta_tags
|
|
|
|
def get_category_meta_tags(self, category):
|
|
"""Meta Tags für Kategorie-Seiten"""
|
|
meta_tags = {
|
|
'title': f"{category.name} Fursuits - Kasico Shop",
|
|
'description': f"Entdecke unsere {category.name} Fursuits. Handgefertigte, hochwertige Fursuits in verschiedenen Stilen und Größen.",
|
|
'keywords': f"{category.name}, fursuit, furry, custom fursuit",
|
|
'og_title': f"{category.name} Fursuits",
|
|
'og_description': f"Entdecke unsere {category.name} Fursuits bei Kasico",
|
|
'og_type': 'website',
|
|
'og_url': f"{self.base_url}{category.get_absolute_url()}",
|
|
'canonical_url': f"{self.base_url}{category.get_absolute_url()}",
|
|
}
|
|
return meta_tags
|
|
|
|
def get_homepage_meta_tags(self):
|
|
"""Meta Tags für Homepage"""
|
|
meta_tags = {
|
|
'title': 'Kasico - Premium Fursuit Shop | Handgefertigte Fursuits',
|
|
'description': 'Entdecke handgefertigte, hochwertige Fursuits bei Kasico. Custom Fursuits, Fullsuits, Headsets und mehr. Made in Germany.',
|
|
'keywords': 'fursuit, furry, custom fursuit, fullsuit, headset, handgefertigt, germany',
|
|
'og_title': 'Kasico - Premium Fursuit Shop',
|
|
'og_description': 'Handgefertigte, hochwertige Fursuits made in Germany',
|
|
'og_type': 'website',
|
|
'og_url': self.base_url,
|
|
'canonical_url': self.base_url,
|
|
}
|
|
return meta_tags
|
|
|
|
def get_structured_data_product(self, product):
|
|
"""Structured Data für Produkte (JSON-LD)"""
|
|
structured_data = {
|
|
"@context": "https://schema.org/",
|
|
"@type": "Product",
|
|
"name": product.name,
|
|
"description": self._clean_description(product.description),
|
|
"image": self._get_product_image_url(product),
|
|
"url": f"{self.base_url}{product.get_absolute_url()}",
|
|
"brand": {
|
|
"@type": "Brand",
|
|
"name": "Kasico"
|
|
},
|
|
"offers": {
|
|
"@type": "Offer",
|
|
"price": str(product.sale_price if product.on_sale else product.base_price),
|
|
"priceCurrency": "EUR",
|
|
"availability": "https://schema.org/InStock" if product.stock > 0 else "https://schema.org/OutOfStock",
|
|
"seller": {
|
|
"@type": "Organization",
|
|
"name": "Kasico"
|
|
}
|
|
},
|
|
"aggregateRating": {
|
|
"@type": "AggregateRating",
|
|
"ratingValue": product.average_rating,
|
|
"reviewCount": product.reviews.count()
|
|
}
|
|
}
|
|
|
|
if product.on_sale:
|
|
structured_data["offers"]["priceSpecification"] = {
|
|
"@type": "PriceSpecification",
|
|
"price": str(product.sale_price),
|
|
"priceCurrency": "EUR",
|
|
"valueAddedTaxIncluded": True
|
|
}
|
|
|
|
return structured_data
|
|
|
|
def get_structured_data_organization(self):
|
|
"""Structured Data für Organisation"""
|
|
structured_data = {
|
|
"@context": "https://schema.org",
|
|
"@type": "Organization",
|
|
"name": "Kasico",
|
|
"url": self.base_url,
|
|
"logo": f"{self.base_url}/static/images/kasico-logo.svg",
|
|
"description": "Premium Fursuit Shop - Handgefertigte Fursuits made in Germany",
|
|
"address": {
|
|
"@type": "PostalAddress",
|
|
"addressCountry": "DE",
|
|
"addressLocality": "Germany"
|
|
},
|
|
"contactPoint": {
|
|
"@type": "ContactPoint",
|
|
"contactType": "customer service",
|
|
"email": "info@kasico.de"
|
|
},
|
|
"sameAs": [
|
|
"https://twitter.com/kasico_fursuits",
|
|
"https://www.instagram.com/kasico_fursuits",
|
|
"https://www.facebook.com/kasico.fursuits"
|
|
]
|
|
}
|
|
return structured_data
|
|
|
|
def get_structured_data_website(self):
|
|
"""Structured Data für Website"""
|
|
structured_data = {
|
|
"@context": "https://schema.org",
|
|
"@type": "WebSite",
|
|
"name": "Kasico Fursuit Shop",
|
|
"url": self.base_url,
|
|
"description": "Premium Fursuit Shop - Handgefertigte Fursuits made in Germany",
|
|
"potentialAction": {
|
|
"@type": "SearchAction",
|
|
"target": f"{self.base_url}/products/search/?q={{search_term_string}}",
|
|
"query-input": "required name=search_term_string"
|
|
}
|
|
}
|
|
return structured_data
|
|
|
|
def get_structured_data_breadcrumb(self, breadcrumbs):
|
|
"""Structured Data für Breadcrumbs"""
|
|
structured_data = {
|
|
"@context": "https://schema.org",
|
|
"@type": "BreadcrumbList",
|
|
"itemListElement": []
|
|
}
|
|
|
|
for i, (name, url) in enumerate(breadcrumbs):
|
|
structured_data["itemListElement"].append({
|
|
"@type": "ListItem",
|
|
"position": i + 1,
|
|
"name": name,
|
|
"item": f"{self.base_url}{url}"
|
|
})
|
|
|
|
return structured_data
|
|
|
|
def generate_sitemap_xml(self):
|
|
"""XML Sitemap generieren"""
|
|
from products.models import Product, Category
|
|
from django.contrib.sitemaps import Sitemap
|
|
|
|
sitemap_data = {
|
|
'urlset': {
|
|
'@xmlns': 'http://www.sitemaps.org/schemas/sitemap/0.9',
|
|
'url': []
|
|
}
|
|
}
|
|
|
|
# Homepage
|
|
sitemap_data['urlset']['url'].append({
|
|
'loc': self.base_url,
|
|
'lastmod': timezone.now().strftime('%Y-%m-%d'),
|
|
'changefreq': 'daily',
|
|
'priority': '1.0'
|
|
})
|
|
|
|
# Produkt-Seiten
|
|
for product in Product.objects.filter(is_active=True):
|
|
sitemap_data['urlset']['url'].append({
|
|
'loc': f"{self.base_url}{product.get_absolute_url()}",
|
|
'lastmod': product.updated_at.strftime('%Y-%m-%d'),
|
|
'changefreq': 'weekly',
|
|
'priority': '0.8'
|
|
})
|
|
|
|
# Kategorie-Seiten
|
|
for category in Category.objects.all():
|
|
sitemap_data['urlset']['url'].append({
|
|
'loc': f"{self.base_url}{category.get_absolute_url()}",
|
|
'lastmod': timezone.now().strftime('%Y-%m-%d'),
|
|
'changefreq': 'weekly',
|
|
'priority': '0.7'
|
|
})
|
|
|
|
# Statische Seiten
|
|
static_pages = [
|
|
('/products/', 'weekly', '0.8'),
|
|
('/gallery/', 'weekly', '0.7'),
|
|
('/contact/', 'monthly', '0.5'),
|
|
('/about/', 'monthly', '0.5'),
|
|
('/faq/', 'monthly', '0.6'),
|
|
]
|
|
|
|
for url, changefreq, priority in static_pages:
|
|
sitemap_data['urlset']['url'].append({
|
|
'loc': f"{self.base_url}{url}",
|
|
'lastmod': timezone.now().strftime('%Y-%m-%d'),
|
|
'changefreq': changefreq,
|
|
'priority': priority
|
|
})
|
|
|
|
return sitemap_data
|
|
|
|
def generate_robots_txt(self):
|
|
"""Robots.txt generieren"""
|
|
robots_content = f"""User-agent: *
|
|
Allow: /
|
|
|
|
# Sitemap
|
|
Sitemap: {self.base_url}/sitemap.xml
|
|
|
|
# Disallow admin and private areas
|
|
Disallow: /admin/
|
|
Disallow: /api/
|
|
Disallow: /static/admin/
|
|
Disallow: /media/admin/
|
|
|
|
# Allow important pages
|
|
Allow: /products/
|
|
Allow: /gallery/
|
|
Allow: /contact/
|
|
Allow: /about/
|
|
Allow: /faq/
|
|
|
|
# Crawl delay
|
|
Crawl-delay: 1
|
|
"""
|
|
return robots_content
|
|
|
|
def _clean_description(self, text, max_length=160):
|
|
"""Beschreibung für Meta Tags bereinigen"""
|
|
if not text:
|
|
return ""
|
|
|
|
# HTML Tags entfernen
|
|
clean_text = strip_tags(text)
|
|
|
|
# Zu lang kürzen
|
|
if len(clean_text) > max_length:
|
|
clean_text = clean_text[:max_length-3] + "..."
|
|
|
|
return clean_text
|
|
|
|
def _get_product_image_url(self, product):
|
|
"""Produkt-Bild URL generieren"""
|
|
if product.image:
|
|
return f"{self.base_url}{product.image.url}"
|
|
return f"{self.base_url}/static/images/default-product.jpg"
|
|
|
|
|
|
class SEOTemplateTags:
|
|
"""Template Tags für SEO"""
|
|
|
|
@staticmethod
|
|
def render_meta_tags(meta_tags):
|
|
"""Meta Tags als HTML rendern"""
|
|
return render_to_string('seo/meta_tags.html', {'meta_tags': meta_tags})
|
|
|
|
@staticmethod
|
|
def render_structured_data(structured_data):
|
|
"""Structured Data als JSON-LD rendern"""
|
|
return render_to_string('seo/structured_data.html', {
|
|
'structured_data': json.dumps(structured_data, indent=2)
|
|
})
|
|
|
|
@staticmethod
|
|
def render_social_meta_tags(meta_tags):
|
|
"""Social Media Meta Tags rendern"""
|
|
return render_to_string('seo/social_meta_tags.html', {'meta_tags': meta_tags})
|
|
|
|
|
|
# SEO Context Processor
|
|
def seo_context_processor(request):
|
|
"""SEO Context für alle Templates"""
|
|
seo_manager = SEOManager(request)
|
|
|
|
# Standard SEO für alle Seiten
|
|
context = {
|
|
'seo_manager': seo_manager,
|
|
'site_name': 'Kasico',
|
|
'site_description': 'Premium Fursuit Shop - Handgefertigte Fursuits made in Germany',
|
|
'site_keywords': 'fursuit, furry, custom fursuit, fullsuit, headset, handgefertigt, germany',
|
|
}
|
|
|
|
# Page-spezifische SEO
|
|
if hasattr(request, 'resolver_match'):
|
|
view_name = request.resolver_match.view_name
|
|
|
|
if 'product_detail' in view_name:
|
|
product = getattr(request, 'product', None)
|
|
if product:
|
|
context.update({
|
|
'meta_tags': seo_manager.get_product_meta_tags(product),
|
|
'structured_data': seo_manager.get_structured_data_product(product)
|
|
})
|
|
|
|
elif 'category_detail' in view_name:
|
|
category = getattr(request, 'category', None)
|
|
if category:
|
|
context.update({
|
|
'meta_tags': seo_manager.get_category_meta_tags(category)
|
|
})
|
|
|
|
elif 'home' in view_name:
|
|
context.update({
|
|
'meta_tags': seo_manager.get_homepage_meta_tags(),
|
|
'structured_data': seo_manager.get_structured_data_website()
|
|
})
|
|
|
|
return context |