focomy 0.1.106__tar.gz → 0.1.108__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {focomy-0.1.106 → focomy-0.1.108}/PKG-INFO +1 -1
- {focomy-0.1.106 → focomy-0.1.108}/core/api/forms.py +3 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/config.py +1 -1
- {focomy-0.1.106 → focomy-0.1.108}/core/engine/routes.py +7 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/main.py +10 -16
- {focomy-0.1.106 → focomy-0.1.108}/core/services/settings.py +10 -0
- focomy-0.1.108/core/utils.py +90 -0
- {focomy-0.1.106 → focomy-0.1.108}/pyproject.toml +1 -1
- {focomy-0.1.106 → focomy-0.1.108}/themes/default/templates/base.html +50 -1
- focomy-0.1.106/core/utils.py +0 -45
- {focomy-0.1.106 → focomy-0.1.108}/.gitignore +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/LICENSE +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/README.md +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/__init__.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/admin/__init__.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/admin/routes.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/admin/url.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/api/__init__.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/api/auth.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/api/comments.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/api/entities.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/api/media.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/api/relations.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/api/revisions.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/api/schema.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/api/search.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/api/seo.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/cli.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/__init__.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/audit_log.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/category.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/channel.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/comment.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/edit_lock.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/form.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/form_submission.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/menu_item.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/news.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/page.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/plugin.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/plugin_developer.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/plugin_review.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/post.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/redirect.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/scheduled_action.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/series.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/site_setting.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/tag.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/user.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/widget.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/content_types/workflow_history.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/database.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/engine/__init__.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/migrations/env.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/migrations/script.py.mako +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/migrations/versions/2038bdf6693b_add_import_jobs_table.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/migrations/versions/3a1b2c3d4e5f_add_file_hash_to_media.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/models/__init__.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/models/auth.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/models/entity.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/models/import_job.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/models/media.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/models/relation.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/models/revision.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/plugins/__init__.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/plugins/base.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/plugins/hooks.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/plugins/loader.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/plugins/manager.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/rate_limit.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/relations.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/scaffold/.env.template +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/scaffold/.gitignore.template +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/scaffold/__init__.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/scaffold/config.yaml.template +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/scaffold/themes/default/templates/archive.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/scaffold/themes/default/templates/base.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/scaffold/themes/default/templates/category.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/scaffold/themes/default/templates/home.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/scaffold/themes/default/templates/post.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/scaffold/themes/default/templates/search.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/scaffold/themes/default/theme.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/schemas/__init__.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/schemas/import_schema.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/seo/__init__.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/__init__.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/api_auth.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/assets.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/audit.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/auth.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/block_converter.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/bulk.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/cache.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/cleanup.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/comment.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/config_priority.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/deployment.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/edit_lock.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/entity.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/export.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/field.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/formula.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/i18n.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/index.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/invite.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/link_validator.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/logging.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/mail.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/marketplace.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/marketplace_verify.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/media.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/media_cleanup.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/menu.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/migration_helpers.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/oauth.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/pagination.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/plugin_resolver.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/plugin_sandbox.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/preview.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/query_optimizer.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/rbac.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/redirect.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/relation.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/revision.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/routing.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/sanitizer.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/schedule.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/search.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/sentry.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/seo.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/spam_filter.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/storage.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/theme.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/theme_inheritance.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/thumbnail.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/update.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/widget.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/wordpress_import/__init__.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/wordpress_import/acf.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/wordpress_import/analyzer.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/wordpress_import/constants.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/wordpress_import/content_sanitizer.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/wordpress_import/diff_detector.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/wordpress_import/dry_run.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/wordpress_import/error_collector.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/wordpress_import/id_resolver.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/wordpress_import/import_service.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/wordpress_import/importer.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/wordpress_import/link_fixer.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/wordpress_import/media.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/wordpress_import/preview.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/wordpress_import/redirects.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/wordpress_import/rest_client.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/wordpress_import/rollback.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/wordpress_import/verification.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/wordpress_import/wxr_parser.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/services/workflow.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/backup.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/base.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/comments.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/components/editor.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/customize.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/dashboard.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/entity_form.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/entity_list.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/forgot_password.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/import.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/link_validator.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/login.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/media.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/menus.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/plugins.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/redirects.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/reset_password.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/settings.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/sitemap.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/system.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/themes.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/templates/admin/widgets.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/themes/__init__.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/themes/customizer.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/themes/manager.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/core/themes/marketplace.py +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/static/favicon.svg +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/blog/templates/base.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/blog/templates/home.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/blog/templates/post.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/blog/theme.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/corporate/templates/base.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/corporate/templates/home.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/corporate/templates/page.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/corporate/theme.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/default/templates/404.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/default/templates/500.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/default/templates/archive.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/default/templates/category.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/default/templates/channel.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/default/templates/form.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/default/templates/form_success.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/default/templates/home.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/default/templates/page.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/default/templates/post.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/default/templates/search.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/default/templates/series.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/default/theme.yaml +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/minimal/templates/base.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/minimal/templates/home.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/minimal/templates/page.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/minimal/templates/post.html +0 -0
- {focomy-0.1.106 → focomy-0.1.108}/themes/minimal/theme.yaml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: focomy
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.108
|
|
4
4
|
Summary: The Most Beautiful CMS - A metadata-driven, zero-duplicate-code content management system
|
|
5
5
|
Project-URL: Homepage, https://github.com/focomy/focomy
|
|
6
6
|
Project-URL: Documentation, https://focomy.dev/docs
|
|
@@ -13,6 +13,7 @@ from ..rate_limit import limiter
|
|
|
13
13
|
from ..services.entity import EntityService
|
|
14
14
|
from ..services.mail import mail_service
|
|
15
15
|
from ..services.theme import theme_service
|
|
16
|
+
from ..utils import require_feature_async
|
|
16
17
|
|
|
17
18
|
router = APIRouter(prefix="/forms", tags=["forms"])
|
|
18
19
|
|
|
@@ -30,6 +31,7 @@ async def view_form(
|
|
|
30
31
|
db: AsyncSession = Depends(get_db),
|
|
31
32
|
):
|
|
32
33
|
"""View a public form."""
|
|
34
|
+
await require_feature_async("form", db)
|
|
33
35
|
entity_svc = EntityService(db)
|
|
34
36
|
|
|
35
37
|
# Find form
|
|
@@ -83,6 +85,7 @@ async def submit_form(
|
|
|
83
85
|
db: AsyncSession = Depends(get_db),
|
|
84
86
|
):
|
|
85
87
|
"""Submit a form."""
|
|
88
|
+
await require_feature_async("form", db)
|
|
86
89
|
entity_svc = EntityService(db)
|
|
87
90
|
|
|
88
91
|
# Find form
|
|
@@ -163,6 +163,13 @@ async def render_theme(
|
|
|
163
163
|
# Build edit URL if entity provided
|
|
164
164
|
if entity and content_type:
|
|
165
165
|
context["edit_url"] = f"/admin/{content_type}/{entity.id}/edit"
|
|
166
|
+
# Add content_types for admin bar dropdown
|
|
167
|
+
all_ct = field_service.get_all_content_types()
|
|
168
|
+
context["content_types"] = {
|
|
169
|
+
name: ct.model_dump()
|
|
170
|
+
for name, ct in all_ct.items()
|
|
171
|
+
if ct.admin_menu # Only show types with admin_menu=true
|
|
172
|
+
}
|
|
166
173
|
else:
|
|
167
174
|
context["is_admin"] = False
|
|
168
175
|
|
|
@@ -452,7 +452,6 @@ async def server_error_handler(request: Request, exc: Exception):
|
|
|
452
452
|
from .admin import routes as admin
|
|
453
453
|
from .api import auth, comments, entities, forms, media, relations, revisions, schema, search, seo
|
|
454
454
|
from .engine import routes as engine
|
|
455
|
-
from .utils import is_feature_enabled
|
|
456
455
|
|
|
457
456
|
# Phase 1: Core APIs (always enabled)
|
|
458
457
|
app.include_router(entities.router, prefix="/api")
|
|
@@ -462,21 +461,16 @@ app.include_router(auth.router, prefix="/api")
|
|
|
462
461
|
app.include_router(seo.router)
|
|
463
462
|
app.include_router(admin.router)
|
|
464
463
|
|
|
465
|
-
# Phase 2: Media
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
# Phase 5: Comments, Forms
|
|
476
|
-
if is_feature_enabled("comment"):
|
|
477
|
-
app.include_router(comments.router, prefix="/api")
|
|
478
|
-
if is_feature_enabled("form"):
|
|
479
|
-
app.include_router(forms.router)
|
|
464
|
+
# Phase 2: Media (runtime check in endpoints)
|
|
465
|
+
app.include_router(media.router, prefix="/api")
|
|
466
|
+
|
|
467
|
+
# Phase 4: Search, Revisions (runtime check in endpoints)
|
|
468
|
+
app.include_router(search.router, prefix="/api")
|
|
469
|
+
app.include_router(revisions.router, prefix="/api")
|
|
470
|
+
|
|
471
|
+
# Phase 5: Comments, Forms (runtime check in endpoints)
|
|
472
|
+
app.include_router(comments.router, prefix="/api")
|
|
473
|
+
app.include_router(forms.router)
|
|
480
474
|
|
|
481
475
|
|
|
482
476
|
@app.get("/api/health")
|
|
@@ -39,6 +39,14 @@ DEFAULT_SETTINGS = {
|
|
|
39
39
|
"lockout_duration": 900,
|
|
40
40
|
"password_min_length": 12,
|
|
41
41
|
},
|
|
42
|
+
"features": {
|
|
43
|
+
"media": True,
|
|
44
|
+
"comment": False,
|
|
45
|
+
"form": True,
|
|
46
|
+
"wordpress_import": False,
|
|
47
|
+
"menu": True,
|
|
48
|
+
"widget": True,
|
|
49
|
+
},
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
|
|
@@ -235,6 +243,7 @@ class SettingsService:
|
|
|
235
243
|
"media": app_settings.media,
|
|
236
244
|
"security": app_settings.security,
|
|
237
245
|
"theme": app_settings.theme,
|
|
246
|
+
"features": app_settings.features,
|
|
238
247
|
}
|
|
239
248
|
|
|
240
249
|
config_obj = config_map.get(category)
|
|
@@ -253,6 +262,7 @@ class SettingsService:
|
|
|
253
262
|
"media": app_settings.media,
|
|
254
263
|
"security": app_settings.security,
|
|
255
264
|
"theme": app_settings.theme,
|
|
265
|
+
"features": app_settings.features,
|
|
256
266
|
}
|
|
257
267
|
|
|
258
268
|
categories = [category] if category else config_map.keys()
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Utility functions for Focomy."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
from functools import lru_cache
|
|
5
|
+
|
|
6
|
+
from fastapi import HTTPException
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def utcnow() -> datetime:
|
|
10
|
+
"""Return current UTC time as naive datetime for DB storage.
|
|
11
|
+
|
|
12
|
+
PostgreSQL TIMESTAMP WITHOUT TIME ZONE expects naive datetimes.
|
|
13
|
+
This function returns UTC time without tzinfo to avoid mismatch errors.
|
|
14
|
+
"""
|
|
15
|
+
return datetime.now(timezone.utc).replace(tzinfo=None)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def is_feature_enabled(feature: str) -> bool:
|
|
19
|
+
"""Check if a feature is enabled.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
feature: Feature name (e.g., 'media', 'comment', 'wordpress_import')
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
True if feature is enabled, False otherwise
|
|
26
|
+
"""
|
|
27
|
+
from .config import get_settings
|
|
28
|
+
|
|
29
|
+
settings = get_settings()
|
|
30
|
+
return getattr(settings.features, feature, False)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def require_feature(feature: str) -> None:
|
|
34
|
+
"""Raise 404 if feature is disabled.
|
|
35
|
+
|
|
36
|
+
Use this at the start of API endpoints to disable them when feature is off.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
feature: Feature name
|
|
40
|
+
|
|
41
|
+
Raises:
|
|
42
|
+
HTTPException: 404 if feature is disabled
|
|
43
|
+
"""
|
|
44
|
+
if not is_feature_enabled(feature):
|
|
45
|
+
raise HTTPException(status_code=404, detail="Not Found")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
async def is_feature_enabled_async(feature: str, db) -> bool:
|
|
49
|
+
"""Check if a feature is enabled (async version, DB-first).
|
|
50
|
+
|
|
51
|
+
Priority: DB settings > config.yaml > False
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
feature: Feature name (e.g., 'form', 'comment')
|
|
55
|
+
db: AsyncSession database session
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
True if feature is enabled, False otherwise
|
|
59
|
+
"""
|
|
60
|
+
from .services.settings import SettingsService
|
|
61
|
+
|
|
62
|
+
settings_svc = SettingsService(db)
|
|
63
|
+
db_value = await settings_svc.get(f"features.{feature}")
|
|
64
|
+
|
|
65
|
+
if db_value is not None:
|
|
66
|
+
# DB value exists, use it
|
|
67
|
+
if isinstance(db_value, bool):
|
|
68
|
+
return db_value
|
|
69
|
+
if isinstance(db_value, str):
|
|
70
|
+
return db_value.lower() in ("true", "1", "yes")
|
|
71
|
+
return bool(db_value)
|
|
72
|
+
|
|
73
|
+
# Fallback to config.yaml
|
|
74
|
+
return is_feature_enabled(feature)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
async def require_feature_async(feature: str, db) -> None:
|
|
78
|
+
"""Raise 404 if feature is disabled (async version, DB-first).
|
|
79
|
+
|
|
80
|
+
Use this at the start of API endpoints for dynamic feature checking.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
feature: Feature name
|
|
84
|
+
db: AsyncSession database session
|
|
85
|
+
|
|
86
|
+
Raises:
|
|
87
|
+
HTTPException: 404 if feature is disabled
|
|
88
|
+
"""
|
|
89
|
+
if not await is_feature_enabled_async(feature, db):
|
|
90
|
+
raise HTTPException(status_code=404, detail="Not Found")
|
|
@@ -123,7 +123,18 @@
|
|
|
123
123
|
<div id="admin-bar">
|
|
124
124
|
<a href="/" class="admin-bar-site">{{ site_name }}</a>
|
|
125
125
|
<a href="/admin">ダッシュボード</a>
|
|
126
|
-
<
|
|
126
|
+
<div class="admin-bar-dropdown">
|
|
127
|
+
<button class="admin-bar-dropdown-btn">+ 新規作成</button>
|
|
128
|
+
<div class="admin-bar-dropdown-menu">
|
|
129
|
+
{% if content_types %}
|
|
130
|
+
{% for name, ct in content_types.items()|sort(attribute='0') %}
|
|
131
|
+
<a href="/admin/{{ name }}/new">{{ ct.label or name }}</a>
|
|
132
|
+
{% endfor %}
|
|
133
|
+
{% else %}
|
|
134
|
+
<a href="/admin/post/new">投稿</a>
|
|
135
|
+
{% endif %}
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
127
138
|
{% if edit_url %}
|
|
128
139
|
<a href="{{ edit_url }}">編集</a>
|
|
129
140
|
{% endif %}
|
|
@@ -163,6 +174,44 @@
|
|
|
163
174
|
.admin-bar-user {
|
|
164
175
|
color: #94a3b8;
|
|
165
176
|
}
|
|
177
|
+
.admin-bar-dropdown {
|
|
178
|
+
position: relative;
|
|
179
|
+
}
|
|
180
|
+
.admin-bar-dropdown-btn {
|
|
181
|
+
background: transparent;
|
|
182
|
+
border: none;
|
|
183
|
+
color: white;
|
|
184
|
+
cursor: pointer;
|
|
185
|
+
font-size: inherit;
|
|
186
|
+
padding: 0;
|
|
187
|
+
}
|
|
188
|
+
.admin-bar-dropdown-btn:hover {
|
|
189
|
+
text-decoration: underline;
|
|
190
|
+
}
|
|
191
|
+
.admin-bar-dropdown-menu {
|
|
192
|
+
display: none;
|
|
193
|
+
position: absolute;
|
|
194
|
+
top: 100%;
|
|
195
|
+
left: 0;
|
|
196
|
+
background: #1e293b;
|
|
197
|
+
border: 1px solid #374151;
|
|
198
|
+
border-radius: 4px;
|
|
199
|
+
min-width: 150px;
|
|
200
|
+
padding: 0.5rem 0;
|
|
201
|
+
box-shadow: 0 4px 6px rgba(0,0,0,0.3);
|
|
202
|
+
}
|
|
203
|
+
.admin-bar-dropdown-menu a {
|
|
204
|
+
display: block;
|
|
205
|
+
padding: 0.5rem 1rem;
|
|
206
|
+
white-space: nowrap;
|
|
207
|
+
}
|
|
208
|
+
.admin-bar-dropdown-menu a:hover {
|
|
209
|
+
background: #374151;
|
|
210
|
+
text-decoration: none;
|
|
211
|
+
}
|
|
212
|
+
.admin-bar-dropdown:hover .admin-bar-dropdown-menu {
|
|
213
|
+
display: block;
|
|
214
|
+
}
|
|
166
215
|
body { padding-top: 32px; }
|
|
167
216
|
</style>
|
|
168
217
|
{% endif %}
|
focomy-0.1.106/core/utils.py
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
"""Utility functions for Focomy."""
|
|
2
|
-
|
|
3
|
-
from datetime import datetime, timezone
|
|
4
|
-
from functools import lru_cache
|
|
5
|
-
|
|
6
|
-
from fastapi import HTTPException
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def utcnow() -> datetime:
|
|
10
|
-
"""Return current UTC time as naive datetime for DB storage.
|
|
11
|
-
|
|
12
|
-
PostgreSQL TIMESTAMP WITHOUT TIME ZONE expects naive datetimes.
|
|
13
|
-
This function returns UTC time without tzinfo to avoid mismatch errors.
|
|
14
|
-
"""
|
|
15
|
-
return datetime.now(timezone.utc).replace(tzinfo=None)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def is_feature_enabled(feature: str) -> bool:
|
|
19
|
-
"""Check if a feature is enabled.
|
|
20
|
-
|
|
21
|
-
Args:
|
|
22
|
-
feature: Feature name (e.g., 'media', 'comment', 'wordpress_import')
|
|
23
|
-
|
|
24
|
-
Returns:
|
|
25
|
-
True if feature is enabled, False otherwise
|
|
26
|
-
"""
|
|
27
|
-
from .config import get_settings
|
|
28
|
-
|
|
29
|
-
settings = get_settings()
|
|
30
|
-
return getattr(settings.features, feature, False)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def require_feature(feature: str) -> None:
|
|
34
|
-
"""Raise 404 if feature is disabled.
|
|
35
|
-
|
|
36
|
-
Use this at the start of API endpoints to disable them when feature is off.
|
|
37
|
-
|
|
38
|
-
Args:
|
|
39
|
-
feature: Feature name
|
|
40
|
-
|
|
41
|
-
Raises:
|
|
42
|
-
HTTPException: 404 if feature is disabled
|
|
43
|
-
"""
|
|
44
|
-
if not is_feature_enabled(feature):
|
|
45
|
-
raise HTTPException(status_code=404, detail="Not Found")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{focomy-0.1.106 → focomy-0.1.108}/core/migrations/versions/2038bdf6693b_add_import_jobs_table.py
RENAMED
|
File without changes
|
{focomy-0.1.106 → focomy-0.1.108}/core/migrations/versions/3a1b2c3d4e5f_add_file_hash_to_media.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|