Modularización de GKACHELE SaaS
This commit is contained in:
1
demo/utils/__init__.py
Normal file
1
demo/utils/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# GKACHELE™ Utils Package
|
||||
54
demo/utils/auth_decorators.py
Normal file
54
demo/utils/auth_decorators.py
Normal file
@@ -0,0 +1,54 @@
|
||||
from functools import wraps
|
||||
from flask import session, request, jsonify, redirect, url_for
|
||||
import sqlite3
|
||||
from config import MAIN_DB
|
||||
|
||||
def login_required(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
if 'user_id' not in session:
|
||||
if request.is_json:
|
||||
return jsonify({'success': False, 'error': 'No autorizado'}), 401
|
||||
return redirect(url_for('auth.login'))
|
||||
return f(*args, **kwargs)
|
||||
return decorated_function
|
||||
|
||||
def user_has_role(user_id, required_role):
|
||||
"""Verificar si usuario tiene un rol específico (desde DB)"""
|
||||
if not user_id:
|
||||
return False
|
||||
|
||||
conn = sqlite3.connect(MAIN_DB)
|
||||
c = conn.cursor()
|
||||
c.execute('SELECT role FROM users WHERE id = ?', (user_id,))
|
||||
result = c.fetchone()
|
||||
conn.close()
|
||||
|
||||
if not result:
|
||||
return False
|
||||
|
||||
user_role = result[0] or 'subscriber'
|
||||
|
||||
# Jerarquía de roles GKACHELE™
|
||||
role_hierarchy = {
|
||||
'administrator': 4,
|
||||
'editor': 3,
|
||||
'author': 2,
|
||||
'subscriber': 1
|
||||
}
|
||||
|
||||
user_level = role_hierarchy.get(user_role, 1)
|
||||
required_level = role_hierarchy.get(required_role, 1)
|
||||
|
||||
return user_level >= required_level
|
||||
|
||||
def user_can(user_id, capability):
|
||||
"""Verificar capacidad específica (desde DB)"""
|
||||
if not user_id:
|
||||
return False
|
||||
|
||||
# Si es admin, puede todo
|
||||
if user_has_role(user_id, 'administrator'):
|
||||
return True
|
||||
|
||||
return False
|
||||
179
demo/utils/theme_engine.py
Normal file
179
demo/utils/theme_engine.py
Normal file
@@ -0,0 +1,179 @@
|
||||
import os
|
||||
import json
|
||||
import sqlite3
|
||||
from flask import current_app
|
||||
from config import THEMES_DIR, MAIN_DB
|
||||
|
||||
def scan_available_themes():
|
||||
"""Escanear todos los templates disponibles y cargar sus configuraciones"""
|
||||
themes = {}
|
||||
if not os.path.exists(THEMES_DIR):
|
||||
return themes
|
||||
|
||||
for theme_dir in os.listdir(THEMES_DIR):
|
||||
theme_path = os.path.join(THEMES_DIR, theme_dir)
|
||||
if not os.path.isdir(theme_path) or theme_dir.startswith('_'):
|
||||
continue
|
||||
|
||||
config_path = os.path.join(theme_path, 'config.json')
|
||||
template_path = os.path.join(theme_path, 'template.html')
|
||||
|
||||
if not os.path.exists(config_path) or not os.path.exists(template_path):
|
||||
continue
|
||||
|
||||
try:
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
config = json.load(f)
|
||||
|
||||
themes[theme_dir] = {
|
||||
'id': theme_dir,
|
||||
'name': config.get('name', theme_dir),
|
||||
'description': config.get('description', ''),
|
||||
'rubro': config.get('rubro', 'general'),
|
||||
'sections': config.get('sections', []),
|
||||
'colors': config.get('colors', {}),
|
||||
'typography': config.get('typography', {}),
|
||||
'features': config.get('features', {}),
|
||||
'preview': f'/themes/{theme_dir}/preview.jpg' if os.path.exists(os.path.join(theme_path, 'preview.jpg')) else None
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"⚠️ Error cargando template {theme_dir}: {e}")
|
||||
continue
|
||||
|
||||
return themes
|
||||
|
||||
def get_theme_config(theme_id):
|
||||
"""Obtener configuración de un template específico"""
|
||||
config_path = os.path.join(THEMES_DIR, theme_id, 'config.json')
|
||||
if not os.path.exists(config_path):
|
||||
return None
|
||||
|
||||
try:
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
except Exception as e:
|
||||
print(f"⚠️ Error cargando config de {theme_id}: {e}")
|
||||
return None
|
||||
|
||||
def get_themes_by_rubro(rubro):
|
||||
"""Obtener templates filtrados por rubro"""
|
||||
all_themes = scan_available_themes()
|
||||
return {k: v for k, v in all_themes.items() if v.get('rubro') == rubro or v.get('rubro') == 'general'}
|
||||
|
||||
def get_site_menus(site_id, user_id):
|
||||
"""Obtener menús del sitio organizados por ubicación"""
|
||||
conn = sqlite3.connect(MAIN_DB)
|
||||
c = conn.cursor()
|
||||
c.execute('''SELECT location, title, url, order_index, parent_id
|
||||
FROM menus
|
||||
WHERE site_id = ? AND user_id = ?
|
||||
ORDER BY location, order_index''', (site_id, user_id))
|
||||
rows = c.fetchall()
|
||||
conn.close()
|
||||
|
||||
menus = {'header': [], 'footer': [], 'sidebar': []}
|
||||
for row in rows:
|
||||
location, title, url, order_index, parent_id = row
|
||||
menu_item = {
|
||||
'title': title,
|
||||
'url': url,
|
||||
'order': order_index,
|
||||
'parent_id': parent_id
|
||||
}
|
||||
if location in menus:
|
||||
menus[location].append(menu_item)
|
||||
|
||||
return menus
|
||||
|
||||
def get_site_widgets(site_id, user_id, area='sidebar'):
|
||||
"""Obtener widgets del sitio por área"""
|
||||
conn = sqlite3.connect(MAIN_DB)
|
||||
c = conn.cursor()
|
||||
c.execute('''SELECT type, title, content, order_index
|
||||
FROM widgets
|
||||
WHERE site_id = ? AND user_id = ? AND area = ?
|
||||
ORDER BY order_index''', (site_id, user_id, area))
|
||||
rows = c.fetchall()
|
||||
conn.close()
|
||||
|
||||
widgets = []
|
||||
for row in rows:
|
||||
widget_type, title, content, order_index = row
|
||||
widgets.append({
|
||||
'type': widget_type,
|
||||
'title': title,
|
||||
'content': content,
|
||||
'order': order_index
|
||||
})
|
||||
|
||||
return widgets
|
||||
|
||||
def render_gkachele_template(theme, content, site_id=None, user_id=None):
|
||||
"""Renderizar template usando estructura GKACHELE (header.php, footer.php, sidebar.php)"""
|
||||
menus = {'header': [], 'footer': [], 'sidebar': []}
|
||||
widgets = []
|
||||
if site_id and user_id:
|
||||
try:
|
||||
menus = get_site_menus(site_id, user_id)
|
||||
widgets = get_site_widgets(site_id, user_id)
|
||||
except Exception as e:
|
||||
print(f"⚠️ Error obteniendo menús/widgets: {e}")
|
||||
|
||||
theme_template = ''
|
||||
theme_path = os.path.join(THEMES_DIR, theme, 'template.html')
|
||||
if os.path.exists(theme_path):
|
||||
with open(theme_path, 'r', encoding='utf-8') as f:
|
||||
theme_template = f.read()
|
||||
else:
|
||||
theme_template = '<div class="container"><h1>{{ hero_title or "Bienvenido" }}</h1></div>'
|
||||
|
||||
header = ''
|
||||
footer = ''
|
||||
sidebar = ''
|
||||
|
||||
header_path = os.path.join(THEMES_DIR, '_gkachele', 'header.php')
|
||||
footer_path = os.path.join(THEMES_DIR, '_gkachele', 'footer.php')
|
||||
sidebar_path = os.path.join(THEMES_DIR, '_gkachele', 'sidebar.php')
|
||||
|
||||
if os.path.exists(header_path):
|
||||
with open(header_path, 'r', encoding='utf-8') as f:
|
||||
header = f.read()
|
||||
else:
|
||||
header = '<html><body>' # Fallback simplificado
|
||||
|
||||
if os.path.exists(footer_path):
|
||||
with open(footer_path, 'r', encoding='utf-8') as f:
|
||||
footer = f.read()
|
||||
else:
|
||||
footer = '</body></html>'
|
||||
|
||||
if os.path.exists(sidebar_path):
|
||||
with open(sidebar_path, 'r', encoding='utf-8') as f:
|
||||
sidebar = f.read()
|
||||
|
||||
template_data = {
|
||||
'site_name': content.get('site_name', 'GKACHELE Site'),
|
||||
'hero_title': content.get('hero_title', 'Bienvenido'),
|
||||
'colors': content.get('colors', {}),
|
||||
'typography': content.get('typography', {}),
|
||||
'horarios': content.get('horarios', {}),
|
||||
'redes_sociales': content.get('redes_sociales', {}),
|
||||
'blocks': content.get('blocks', []),
|
||||
'menus': menus,
|
||||
'widgets': widgets,
|
||||
**content
|
||||
}
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
is_full_page = False
|
||||
if theme == 'restaurante-moderno' or '<!DOCTYPE html>' in theme_template:
|
||||
is_full_page = True
|
||||
|
||||
if is_full_page:
|
||||
template = Template(theme_template)
|
||||
return template.render(**template_data)
|
||||
|
||||
full_template = header + theme_template + sidebar + footer
|
||||
template = Template(full_template)
|
||||
return template.render(**template_data)
|
||||
Reference in New Issue
Block a user