Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
Charts now render automatically on page load via AJAX from DB cache (no click needed). Info bar above charts shows post count, cache date, and hint to refresh. GET cache endpoint now returns cached_at date. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
660 lines
27 KiB
Python
660 lines
27 KiB
Python
"""
|
|
Admin Social Publisher Routes
|
|
==============================
|
|
|
|
Social media publishing management with per-company Facebook configuration.
|
|
"""
|
|
|
|
import logging
|
|
import os
|
|
from datetime import datetime
|
|
|
|
from flask import render_template, request, redirect, url_for, flash, jsonify
|
|
from flask_login import login_required, current_user
|
|
|
|
from . import bp
|
|
from database import (SessionLocal, SocialPost, SocialMediaConfig, Company,
|
|
NordaEvent, SystemRole, OAuthToken, UserCompanyPermissions)
|
|
from utils.decorators import role_required
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def _is_admin():
|
|
"""Check if current user is ADMIN or higher."""
|
|
return current_user.system_role >= SystemRole.ADMIN
|
|
|
|
|
|
def _get_user_company_ids(db):
|
|
"""Get list of company IDs the current user has access to."""
|
|
if _is_admin():
|
|
return None # None = all companies
|
|
|
|
company_ids = set()
|
|
if current_user.company_id:
|
|
company_ids.add(current_user.company_id)
|
|
|
|
perms = db.query(UserCompanyPermissions).filter(
|
|
UserCompanyPermissions.user_id == current_user.id
|
|
).all()
|
|
for p in perms:
|
|
company_ids.add(p.company_id)
|
|
|
|
return list(company_ids)
|
|
|
|
|
|
def _get_user_companies(db):
|
|
"""Get companies the current user has access to for social publishing."""
|
|
company_ids = _get_user_company_ids(db)
|
|
if company_ids is None:
|
|
return db.query(Company).filter(Company.status == 'active').order_by(Company.name).all()
|
|
if not company_ids:
|
|
return []
|
|
return db.query(Company).filter(
|
|
Company.id.in_(company_ids),
|
|
Company.status == 'active'
|
|
).order_by(Company.name).all()
|
|
|
|
|
|
def _user_can_access_company(db, company_id):
|
|
"""Check if current user can manage social publisher for a company."""
|
|
if _is_admin():
|
|
return True
|
|
company_ids = _get_user_company_ids(db)
|
|
return company_ids and company_id in company_ids
|
|
|
|
|
|
# ============================================================
|
|
# SOCIAL PUBLISHER - LIST & DASHBOARD
|
|
# ============================================================
|
|
|
|
@bp.route('/social-publisher')
|
|
@login_required
|
|
@role_required(SystemRole.MANAGER)
|
|
def social_publisher_list():
|
|
"""Lista postow social media z filtrami."""
|
|
from services.social_publisher_service import social_publisher, POST_TYPES
|
|
|
|
status_filter = request.args.get('status', 'all')
|
|
type_filter = request.args.get('type', 'all')
|
|
company_filter = request.args.get('company', 'all')
|
|
|
|
db = SessionLocal()
|
|
try:
|
|
query = db.query(SocialPost).order_by(SocialPost.created_at.desc())
|
|
|
|
# Manager widzi tylko posty swojej firmy
|
|
user_company_ids = _get_user_company_ids(db)
|
|
if user_company_ids is not None:
|
|
query = query.filter(SocialPost.publishing_company_id.in_(user_company_ids))
|
|
|
|
if status_filter != 'all':
|
|
query = query.filter(SocialPost.status == status_filter)
|
|
if type_filter != 'all':
|
|
query = query.filter(SocialPost.post_type == type_filter)
|
|
if company_filter != 'all':
|
|
try:
|
|
query = query.filter(SocialPost.publishing_company_id == int(company_filter))
|
|
except (ValueError, TypeError):
|
|
pass
|
|
|
|
posts = query.limit(100).all()
|
|
stats = social_publisher.get_stats()
|
|
configured_companies = social_publisher.get_configured_companies(user_company_ids)
|
|
|
|
# Load Facebook API stats for connected companies
|
|
fb_stats = {}
|
|
if user_company_ids:
|
|
from database import CompanySocialMedia
|
|
fb_profiles = db.query(CompanySocialMedia).filter(
|
|
CompanySocialMedia.company_id.in_(user_company_ids),
|
|
CompanySocialMedia.platform == 'facebook',
|
|
CompanySocialMedia.source == 'facebook_api',
|
|
).all()
|
|
for fp in fb_profiles:
|
|
fb_stats[fp.company_id] = fp
|
|
|
|
# Load cached FB posts from DB for instant rendering
|
|
# Inline max 10 posts for fast page load; full set via AJAX "Analityka"
|
|
cached_fb_posts = {}
|
|
cache_company_ids = user_company_ids if user_company_ids is not None else list(fb_stats.keys())
|
|
for cid in cache_company_ids:
|
|
cached = social_publisher.get_cached_posts(cid)
|
|
if cached:
|
|
cached_fb_posts[cid] = {
|
|
'posts': cached['posts'][:10],
|
|
'cached_at': cached['cached_at'],
|
|
'total_count': cached['total_count'],
|
|
}
|
|
|
|
return render_template('admin/social_publisher.html',
|
|
posts=posts,
|
|
stats=stats,
|
|
post_types=POST_TYPES,
|
|
status_filter=status_filter,
|
|
type_filter=type_filter,
|
|
company_filter=company_filter,
|
|
configured_companies=configured_companies,
|
|
fb_stats=fb_stats,
|
|
cached_fb_posts=cached_fb_posts)
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
# ============================================================
|
|
# SOCIAL PUBLISHER - CREATE / EDIT FORM
|
|
# ============================================================
|
|
|
|
@bp.route('/social-publisher/new', methods=['GET', 'POST'])
|
|
@login_required
|
|
@role_required(SystemRole.MANAGER)
|
|
def social_publisher_new():
|
|
"""Tworzenie nowego posta."""
|
|
from services.social_publisher_service import social_publisher, POST_TYPES, POST_TONES, DEFAULT_TONE, AI_MODELS, DEFAULT_AI_MODEL
|
|
|
|
db = SessionLocal()
|
|
try:
|
|
user_company_ids = _get_user_company_ids(db)
|
|
companies = _get_user_companies(db)
|
|
events = db.query(NordaEvent).order_by(NordaEvent.event_date.desc()).limit(20).all()
|
|
configured_companies = social_publisher.get_configured_companies(user_company_ids)
|
|
|
|
if request.method == 'POST':
|
|
action = request.form.get('action', 'draft')
|
|
post_type = request.form.get('post_type', 'chamber_news')
|
|
content = request.form.get('content', '').strip()
|
|
hashtags = request.form.get('hashtags', '').strip()
|
|
company_id = request.form.get('company_id', type=int)
|
|
event_id = request.form.get('event_id', type=int)
|
|
publishing_company_id = request.form.get('publishing_company_id', type=int)
|
|
|
|
# Default to first company if not selected
|
|
if not publishing_company_id and companies:
|
|
publishing_company_id = companies[0].id
|
|
|
|
if not content:
|
|
flash('Treść posta jest wymagana.', 'danger')
|
|
return render_template('admin/social_publisher_form.html',
|
|
post=None, companies=companies, events=events,
|
|
post_types=POST_TYPES, post_tones=POST_TONES, default_tone=DEFAULT_TONE,
|
|
ai_models=AI_MODELS, default_ai_model=DEFAULT_AI_MODEL,
|
|
configured_companies=configured_companies)
|
|
|
|
post = social_publisher.create_post(
|
|
post_type=post_type,
|
|
content=content,
|
|
hashtags=hashtags or None,
|
|
company_id=company_id,
|
|
event_id=event_id,
|
|
publishing_company_id=publishing_company_id,
|
|
user_id=current_user.id,
|
|
)
|
|
|
|
if action in ('publish', 'publish_live'):
|
|
force_live = (action == 'publish_live')
|
|
success, message = social_publisher.publish_post(post.id, force_live=force_live)
|
|
flash(message, 'success' if success else 'danger')
|
|
else:
|
|
flash('Post utworzony (szkic).', 'success')
|
|
|
|
return redirect(url_for('admin.social_publisher_edit', post_id=post.id))
|
|
|
|
return render_template('admin/social_publisher_form.html',
|
|
post=None, companies=companies, events=events,
|
|
post_types=POST_TYPES, post_tones=POST_TONES, default_tone=DEFAULT_TONE,
|
|
ai_models=AI_MODELS, default_ai_model=DEFAULT_AI_MODEL,
|
|
configured_companies=configured_companies)
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@bp.route('/social-publisher/<int:post_id>/edit', methods=['GET', 'POST'])
|
|
@login_required
|
|
@role_required(SystemRole.MANAGER)
|
|
def social_publisher_edit(post_id):
|
|
"""Edycja istniejacego posta."""
|
|
from services.social_publisher_service import social_publisher, POST_TYPES, POST_TONES, DEFAULT_TONE, AI_MODELS, DEFAULT_AI_MODEL
|
|
|
|
db = SessionLocal()
|
|
try:
|
|
post = db.query(SocialPost).filter(SocialPost.id == post_id).first()
|
|
if not post:
|
|
flash('Post nie znaleziony.', 'danger')
|
|
return redirect(url_for('admin.social_publisher_list'))
|
|
|
|
# Manager moze edytowac tylko posty swojej firmy
|
|
user_company_ids = _get_user_company_ids(db)
|
|
if user_company_ids is not None and post.publishing_company_id not in user_company_ids:
|
|
flash('Nie masz uprawnień do edycji tego posta.', 'danger')
|
|
return redirect(url_for('admin.social_publisher_list'))
|
|
|
|
companies = _get_user_companies(db)
|
|
events = db.query(NordaEvent).order_by(NordaEvent.event_date.desc()).limit(20).all()
|
|
configured_companies = social_publisher.get_configured_companies(user_company_ids)
|
|
|
|
if request.method == 'POST':
|
|
action = request.form.get('action', 'save')
|
|
|
|
# Handle non-edit actions
|
|
if action == 'approve':
|
|
result = social_publisher.approve_post(post_id, current_user.id)
|
|
flash('Post zatwierdzony.' if result else 'Nie można zatwierdzić posta.',
|
|
'success' if result else 'danger')
|
|
return redirect(url_for('admin.social_publisher_edit', post_id=post_id))
|
|
|
|
if action in ('publish', 'publish_live'):
|
|
force_live = (action == 'publish_live')
|
|
success, message = social_publisher.publish_post(post_id, force_live=force_live)
|
|
flash(message, 'success' if success else 'danger')
|
|
return redirect(url_for('admin.social_publisher_edit', post_id=post_id))
|
|
|
|
if action == 'delete':
|
|
if social_publisher.delete_post(post_id):
|
|
flash('Post usunięty.', 'success')
|
|
return redirect(url_for('admin.social_publisher_list'))
|
|
flash('Nie można usunąć posta.', 'danger')
|
|
return redirect(url_for('admin.social_publisher_edit', post_id=post_id))
|
|
|
|
# Default: save/update
|
|
content = request.form.get('content', '').strip()
|
|
hashtags = request.form.get('hashtags', '').strip()
|
|
post_type = request.form.get('post_type')
|
|
company_id = request.form.get('company_id', type=int)
|
|
event_id = request.form.get('event_id', type=int)
|
|
publishing_company_id = request.form.get('publishing_company_id', type=int)
|
|
|
|
if not content:
|
|
flash('Treść posta jest wymagana.', 'danger')
|
|
return render_template('admin/social_publisher_form.html',
|
|
post=post, companies=companies, events=events,
|
|
post_types=POST_TYPES, post_tones=POST_TONES, default_tone=DEFAULT_TONE,
|
|
ai_models=AI_MODELS, default_ai_model=DEFAULT_AI_MODEL,
|
|
configured_companies=configured_companies)
|
|
|
|
social_publisher.update_post(
|
|
post_id=post_id,
|
|
content=content,
|
|
hashtags=hashtags or None,
|
|
post_type=post_type,
|
|
company_id=company_id,
|
|
event_id=event_id,
|
|
publishing_company_id=publishing_company_id,
|
|
)
|
|
flash('Post zaktualizowany.', 'success')
|
|
return redirect(url_for('admin.social_publisher_edit', post_id=post_id))
|
|
|
|
return render_template('admin/social_publisher_form.html',
|
|
post=post, companies=companies, events=events,
|
|
post_types=POST_TYPES, post_tones=POST_TONES, default_tone=DEFAULT_TONE,
|
|
ai_models=AI_MODELS, default_ai_model=DEFAULT_AI_MODEL,
|
|
configured_companies=configured_companies)
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
# ============================================================
|
|
# SOCIAL PUBLISHER - ACTIONS
|
|
# ============================================================
|
|
|
|
@bp.route('/social-publisher/<int:post_id>/approve', methods=['POST'])
|
|
@login_required
|
|
@role_required(SystemRole.MANAGER)
|
|
def social_publisher_approve(post_id):
|
|
"""Zatwierdz post."""
|
|
from services.social_publisher_service import social_publisher
|
|
|
|
result = social_publisher.approve_post(post_id, current_user.id)
|
|
if result:
|
|
flash('Post zatwierdzony.', 'success')
|
|
else:
|
|
flash('Nie można zatwierdzić posta.', 'danger')
|
|
return redirect(url_for('admin.social_publisher_edit', post_id=post_id))
|
|
|
|
|
|
@bp.route('/social-publisher/<int:post_id>/publish', methods=['POST'])
|
|
@login_required
|
|
@role_required(SystemRole.MANAGER)
|
|
def social_publisher_publish(post_id):
|
|
"""Opublikuj post na Facebook."""
|
|
from services.social_publisher_service import social_publisher
|
|
|
|
success, message = social_publisher.publish_post(post_id)
|
|
|
|
# AJAX response
|
|
if request.headers.get('X-CSRFToken') and not request.form:
|
|
return jsonify({'success': success, 'message': message, 'error': None if success else message})
|
|
|
|
flash(message, 'success' if success else 'danger')
|
|
return redirect(url_for('admin.social_publisher_edit', post_id=post_id))
|
|
|
|
|
|
@bp.route('/social-publisher/<int:post_id>/delete', methods=['POST'])
|
|
@login_required
|
|
@role_required(SystemRole.MANAGER)
|
|
def social_publisher_delete(post_id):
|
|
"""Usuń post."""
|
|
from services.social_publisher_service import social_publisher
|
|
|
|
success = social_publisher.delete_post(post_id)
|
|
|
|
# AJAX response
|
|
if request.headers.get('X-CSRFToken') and not request.form:
|
|
return jsonify({'success': success, 'error': None if success else 'Nie można usunąć posta.'})
|
|
|
|
flash('Post usunięty.' if success else 'Nie można usunąć posta.', 'success' if success else 'danger')
|
|
return redirect(url_for('admin.social_publisher_list'))
|
|
|
|
|
|
@bp.route('/social-publisher/<int:post_id>/toggle-visibility', methods=['POST'])
|
|
@login_required
|
|
@role_required(SystemRole.MANAGER)
|
|
def social_publisher_toggle_visibility(post_id):
|
|
"""Opublikuj draft post publicznie na Facebook (debug -> live)."""
|
|
from services.social_publisher_service import social_publisher
|
|
|
|
success, message = social_publisher.toggle_visibility(post_id)
|
|
flash(message, 'success' if success else 'danger')
|
|
return redirect(url_for('admin.social_publisher_edit', post_id=post_id))
|
|
|
|
|
|
@bp.route('/social-publisher/<int:post_id>/withdraw', methods=['POST'])
|
|
@login_required
|
|
@role_required(SystemRole.MANAGER)
|
|
def social_publisher_withdraw(post_id):
|
|
"""Usun post z Facebooka i przywroc do szkicu."""
|
|
from services.social_publisher_service import social_publisher
|
|
|
|
success, message = social_publisher.withdraw_from_fb(post_id)
|
|
flash(message, 'success' if success else 'danger')
|
|
return redirect(url_for('admin.social_publisher_edit', post_id=post_id))
|
|
|
|
|
|
@bp.route('/social-publisher/<int:post_id>/refresh-engagement', methods=['POST'])
|
|
@login_required
|
|
@role_required(SystemRole.MANAGER)
|
|
def social_publisher_refresh_engagement(post_id):
|
|
"""Odswiez metryki engagement z Facebook."""
|
|
from services.social_publisher_service import social_publisher
|
|
|
|
result = social_publisher.refresh_engagement(post_id)
|
|
if result:
|
|
flash('Engagement zaktualizowany.', 'success')
|
|
else:
|
|
flash('Nie udało się pobrać engagement.', 'warning')
|
|
return redirect(url_for('admin.social_publisher_edit', post_id=post_id))
|
|
|
|
|
|
# ============================================================
|
|
# SOCIAL PUBLISHER - FACEBOOK PAGE POSTS (AJAX)
|
|
# ============================================================
|
|
|
|
@bp.route('/social-publisher/fb-posts/<int:company_id>')
|
|
@login_required
|
|
@role_required(SystemRole.MANAGER)
|
|
def social_publisher_fb_posts(company_id):
|
|
"""Pobierz ostatnie posty z Facebook Page (AJAX)."""
|
|
from services.social_publisher_service import social_publisher
|
|
|
|
db = SessionLocal()
|
|
try:
|
|
if not _user_can_access_company(db, company_id):
|
|
return jsonify({'success': False, 'error': 'Brak uprawnień.'}), 403
|
|
finally:
|
|
db.close()
|
|
|
|
after = request.args.get('after')
|
|
result = social_publisher.get_page_recent_posts(company_id, after=after)
|
|
return jsonify(result)
|
|
|
|
|
|
@bp.route('/social-publisher/fb-posts-cache/<int:company_id>', methods=['GET'])
|
|
@login_required
|
|
@role_required(SystemRole.MANAGER)
|
|
def social_publisher_get_posts_cache(company_id):
|
|
"""Return full cached posts from DB (AJAX, for Analityka button)."""
|
|
from services.social_publisher_service import social_publisher
|
|
|
|
db = SessionLocal()
|
|
try:
|
|
if not _user_can_access_company(db, company_id):
|
|
return jsonify({'success': False, 'error': 'Brak uprawnień.'}), 403
|
|
finally:
|
|
db.close()
|
|
|
|
cached = social_publisher.get_cached_posts(company_id)
|
|
if cached and cached['posts']:
|
|
cached_at = cached['cached_at'].strftime('%d.%m.%Y %H:%M') if cached.get('cached_at') else None
|
|
return jsonify({'success': True, 'posts': cached['posts'], 'total_count': cached['total_count'], 'cached_at': cached_at})
|
|
return jsonify({'success': False, 'error': 'Brak danych w cache. Kliknij Analityka aby pobrac.'})
|
|
|
|
|
|
@bp.route('/social-publisher/fb-posts-cache/<int:company_id>', methods=['POST'])
|
|
@login_required
|
|
@role_required(SystemRole.MANAGER)
|
|
def social_publisher_save_posts_cache(company_id):
|
|
"""Save all loaded FB posts to DB cache for instant page load (AJAX)."""
|
|
from services.social_publisher_service import social_publisher
|
|
|
|
db = SessionLocal()
|
|
try:
|
|
if not _user_can_access_company(db, company_id):
|
|
return jsonify({'success': False, 'error': 'Brak uprawnień.'}), 403
|
|
finally:
|
|
db.close()
|
|
|
|
data = request.get_json()
|
|
posts = data.get('posts', []) if data else []
|
|
if posts:
|
|
social_publisher.save_all_posts_to_cache(company_id, posts)
|
|
return jsonify({'success': True, 'saved': len(posts)})
|
|
|
|
|
|
@bp.route('/social-publisher/fb-post-insights/<int:company_id>/<path:post_id>')
|
|
@login_required
|
|
@role_required(SystemRole.MANAGER)
|
|
def social_publisher_fb_post_insights(company_id, post_id):
|
|
"""Pobierz insights dla konkretnego posta (AJAX)."""
|
|
from services.social_publisher_service import social_publisher
|
|
|
|
db = SessionLocal()
|
|
try:
|
|
if not _user_can_access_company(db, company_id):
|
|
return jsonify({'success': False, 'error': 'Brak uprawnień.'}), 403
|
|
finally:
|
|
db.close()
|
|
|
|
result = social_publisher.get_post_insights_detail(company_id, post_id)
|
|
return jsonify(result)
|
|
|
|
|
|
# ============================================================
|
|
# SOCIAL PUBLISHER - AI GENERATION (AJAX)
|
|
# ============================================================
|
|
|
|
@bp.route('/social-publisher/generate', methods=['POST'])
|
|
@login_required
|
|
@role_required(SystemRole.MANAGER)
|
|
def social_publisher_generate():
|
|
"""Generuj tresc posta AI (AJAX endpoint)."""
|
|
from services.social_publisher_service import social_publisher
|
|
|
|
post_type = request.json.get('post_type')
|
|
company_id = request.json.get('company_id')
|
|
event_id = request.json.get('event_id')
|
|
tone = request.json.get('tone', '')
|
|
ai_model = request.json.get('ai_model', '')
|
|
custom_context = request.json.get('custom_context', {})
|
|
|
|
try:
|
|
# Build context
|
|
context = {}
|
|
if company_id:
|
|
context.update(social_publisher.get_company_context(int(company_id)))
|
|
if event_id:
|
|
context.update(social_publisher.get_event_context(int(event_id)))
|
|
context.update(custom_context)
|
|
|
|
# Fill defaults for missing fields
|
|
defaults = {
|
|
'company_name': '', 'category': '', 'city': 'Wejherowo',
|
|
'description': '', 'website': '', 'social_media_links': '',
|
|
'event_title': '', 'event_date': '', 'event_location': '',
|
|
'event_description': '', 'event_topics': '', 'attendees_count': '',
|
|
'topic': '', 'source': '', 'facts': '', 'details': '',
|
|
}
|
|
for key, default in defaults.items():
|
|
context.setdefault(key, default)
|
|
|
|
pub_company_id = request.json.get('publishing_company_id')
|
|
content, hashtags, model = social_publisher.generate_content(
|
|
post_type, context, tone=tone, ai_model=ai_model,
|
|
user_id=current_user.id,
|
|
company_id=int(pub_company_id) if pub_company_id else (int(company_id) if company_id else None),
|
|
)
|
|
return jsonify({'success': True, 'content': content, 'hashtags': hashtags, 'model': model})
|
|
except Exception as e:
|
|
logger.error(f"AI generation failed: {e}")
|
|
return jsonify({'success': False, 'error': 'Nie udalo sie wygenerowac tresci. Sprobuj ponownie lub wpisz tresc recznie.'}), 500
|
|
|
|
|
|
@bp.route('/social-publisher/generate-hashtags', methods=['POST'])
|
|
@login_required
|
|
@role_required(SystemRole.MANAGER)
|
|
def social_publisher_generate_hashtags():
|
|
"""Generuj hashtagi AI na podstawie tresci posta (AJAX endpoint)."""
|
|
from services.social_publisher_service import social_publisher
|
|
|
|
content = request.json.get('content', '').strip()
|
|
post_type = request.json.get('post_type', '')
|
|
ai_model = request.json.get('ai_model', '')
|
|
|
|
if not content:
|
|
return jsonify({'success': False, 'error': 'Wpisz najpierw tresc posta, aby wygenerowac hashtagi.'}), 400
|
|
|
|
try:
|
|
hashtags, model = social_publisher.generate_hashtags(
|
|
content, post_type, ai_model=ai_model,
|
|
user_id=current_user.id,
|
|
)
|
|
return jsonify({'success': True, 'hashtags': hashtags, 'model': model})
|
|
except Exception as e:
|
|
logger.error(f"Hashtag generation failed: {e}")
|
|
return jsonify({'success': False, 'error': 'Nie udalo sie wygenerowac hashtagow. Sprobuj ponownie.'}), 500
|
|
|
|
|
|
# ============================================================
|
|
# SOCIAL PUBLISHER - SETTINGS (per company)
|
|
# ============================================================
|
|
|
|
@bp.route('/social-publisher/settings')
|
|
@login_required
|
|
@role_required(SystemRole.MANAGER)
|
|
def social_publisher_settings():
|
|
"""Lista firm z konfiguracjami Facebook."""
|
|
from services.social_publisher_service import social_publisher
|
|
|
|
db = SessionLocal()
|
|
try:
|
|
user_companies = _get_user_companies(db)
|
|
|
|
# Get existing configs for each company
|
|
company_configs = []
|
|
for company in user_companies:
|
|
config = db.query(SocialMediaConfig).filter(
|
|
SocialMediaConfig.platform == 'facebook',
|
|
SocialMediaConfig.company_id == company.id,
|
|
).first()
|
|
|
|
# Check OAuth connection
|
|
oauth_token = db.query(OAuthToken).filter(
|
|
OAuthToken.company_id == company.id,
|
|
OAuthToken.provider == 'meta',
|
|
OAuthToken.service == 'facebook',
|
|
OAuthToken.is_active == True,
|
|
).first()
|
|
|
|
company_configs.append({
|
|
'company': company,
|
|
'config': config,
|
|
'oauth_connected': bool(oauth_token),
|
|
'oauth_page_name': oauth_token.account_name if oauth_token else None,
|
|
})
|
|
|
|
return render_template('admin/social_publisher_settings.html',
|
|
company_configs=company_configs)
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@bp.route('/social-publisher/settings/<int:company_id>', methods=['GET', 'POST'])
|
|
@login_required
|
|
@role_required(SystemRole.MANAGER)
|
|
def social_publisher_company_settings(company_id):
|
|
"""Konfiguracja Facebook dla konkretnej firmy."""
|
|
from services.social_publisher_service import social_publisher
|
|
|
|
db = SessionLocal()
|
|
try:
|
|
company = db.query(Company).filter(Company.id == company_id).first()
|
|
if not company:
|
|
flash('Firma nie znaleziona.', 'danger')
|
|
return redirect(url_for('admin.social_publisher_settings'))
|
|
|
|
if not _user_can_access_company(db, company_id):
|
|
flash('Nie masz uprawnień do konfiguracji tej firmy.', 'danger')
|
|
return redirect(url_for('admin.social_publisher_settings'))
|
|
|
|
config = db.query(SocialMediaConfig).filter(
|
|
SocialMediaConfig.platform == 'facebook',
|
|
SocialMediaConfig.company_id == company_id,
|
|
).first()
|
|
|
|
oauth_token = db.query(OAuthToken).filter(
|
|
OAuthToken.company_id == company_id,
|
|
OAuthToken.provider == 'meta',
|
|
OAuthToken.service == 'facebook',
|
|
OAuthToken.is_active == True,
|
|
).first()
|
|
|
|
if request.method == 'POST':
|
|
debug_mode = request.form.get('debug_mode') == 'on'
|
|
|
|
# If we have OAuth + config with page, just update debug_mode
|
|
if config and config.page_id:
|
|
social_publisher.save_fb_config(
|
|
company_id=company_id,
|
|
page_id=config.page_id,
|
|
page_name=config.page_name or '',
|
|
debug_mode=debug_mode,
|
|
user_id=current_user.id,
|
|
)
|
|
flash('Ustawienia zapisane.', 'success')
|
|
else:
|
|
# Manual config (legacy)
|
|
page_id = request.form.get('page_id', '').strip()
|
|
page_name = request.form.get('page_name', '').strip()
|
|
access_token = request.form.get('access_token', '').strip()
|
|
|
|
if not page_id:
|
|
flash('Page ID jest wymagany.', 'danger')
|
|
else:
|
|
social_publisher.save_fb_config(
|
|
company_id=company_id,
|
|
page_id=page_id,
|
|
page_name=page_name,
|
|
debug_mode=debug_mode,
|
|
user_id=current_user.id,
|
|
access_token=access_token or None,
|
|
)
|
|
flash('Konfiguracja Facebook zapisana.', 'success')
|
|
|
|
return redirect(url_for('admin.social_publisher_company_settings',
|
|
company_id=company_id))
|
|
|
|
return render_template('admin/social_publisher_company_settings.html',
|
|
company=company,
|
|
config=config,
|
|
oauth_token=oauth_token)
|
|
finally:
|
|
db.close()
|