focomy 0.1.113__py3-none-any.whl → 0.1.115__py3-none-any.whl

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.
core/admin/routes.py CHANGED
@@ -589,11 +589,22 @@ async def dashboard(
589
589
  data["channel_title"] = channel_data.get("title")
590
590
  recent_posts.append(data)
591
591
 
592
+ # Check for updates
593
+ from ..services.update import update_service
594
+
595
+ update_info = await update_service.check_for_updates()
596
+
592
597
  context = await get_context(request, db, current_user, "dashboard")
593
598
  context.update(
594
599
  {
595
600
  "stats": stats,
596
601
  "recent_posts": recent_posts,
602
+ "update_info": {
603
+ "current_version": update_info.current_version,
604
+ "latest_version": update_info.latest_version,
605
+ "has_update": update_info.has_update,
606
+ "release_url": update_info.release_url,
607
+ },
597
608
  }
598
609
  )
599
610
 
@@ -4177,6 +4188,16 @@ async def entity_create(
4177
4188
  if rel_ids:
4178
4189
  await relation_svc.sync(entity.id, rel_ids, rel.type)
4179
4190
 
4191
+ # Auto-assign posts channel for post type if not specified
4192
+ if type_name == "post":
4193
+ rel_channel_values = form_data.getlist("rel_post_channel")
4194
+ channel_ids = [v for v in rel_channel_values if v]
4195
+ if not channel_ids:
4196
+ from ..services.channel import get_or_create_posts_channel
4197
+
4198
+ posts_channel_id = await get_or_create_posts_channel(db)
4199
+ await relation_svc.sync(entity.id, [posts_channel_id], "post_channel")
4200
+
4180
4201
  # Log create action
4181
4202
  audit_svc = AuditService(db)
4182
4203
  await audit_svc.log_create(
@@ -4484,6 +4505,16 @@ async def entity_delete(
4484
4505
  entity_data = entity_svc.serialize(entity)
4485
4506
  user_data = entity_svc.serialize(current_user)
4486
4507
 
4508
+ # Check if channel is protected from deletion
4509
+ if type_name == "channel":
4510
+ from ..services.channel import is_protected_channel
4511
+
4512
+ if is_protected_channel(entity_data.get("slug", "")):
4513
+ raise HTTPException(
4514
+ status_code=400,
4515
+ detail="postsチャンネルは削除できません",
4516
+ )
4517
+
4487
4518
  await entity_svc.delete(entity_id, user_id=user_data.get("id"))
4488
4519
 
4489
4520
  # Log delete action
@@ -4527,6 +4558,16 @@ async def entity_delete_post(
4527
4558
  entity_data = entity_svc.serialize(entity)
4528
4559
  user_data = entity_svc.serialize(current_user)
4529
4560
 
4561
+ # Check if channel is protected from deletion
4562
+ if type_name == "channel":
4563
+ from ..services.channel import is_protected_channel
4564
+
4565
+ if is_protected_channel(entity_data.get("slug", "")):
4566
+ raise HTTPException(
4567
+ status_code=400,
4568
+ detail="postsチャンネルは削除できません",
4569
+ )
4570
+
4530
4571
  await entity_svc.delete(entity_id, user_id=user_data.get("id"))
4531
4572
 
4532
4573
  # Log delete action
@@ -0,0 +1,58 @@
1
+ """Channel service - Default channel management."""
2
+
3
+ import structlog
4
+
5
+ from sqlalchemy.ext.asyncio import AsyncSession
6
+
7
+ from .entity import EntityService
8
+
9
+ logger = structlog.get_logger(__name__)
10
+
11
+ DEFAULT_CHANNEL_SLUG = "posts"
12
+
13
+
14
+ async def get_or_create_posts_channel(db: AsyncSession) -> str:
15
+ """Get or create the default 'posts' channel.
16
+
17
+ Returns:
18
+ Channel entity ID
19
+ """
20
+ entity_svc = EntityService(db)
21
+
22
+ # Try to find existing posts channel
23
+ entities = await entity_svc.find(
24
+ "channel",
25
+ filters={"slug": DEFAULT_CHANNEL_SLUG},
26
+ limit=1,
27
+ )
28
+
29
+ if entities:
30
+ channel_id = entities[0].id
31
+ logger.debug("found_posts_channel", channel_id=channel_id)
32
+ return channel_id
33
+
34
+ # Create posts channel
35
+ entity = await entity_svc.create(
36
+ "channel",
37
+ {
38
+ "title": "Posts",
39
+ "slug": DEFAULT_CHANNEL_SLUG,
40
+ "description": "Default channel for posts",
41
+ "sort_order": 0,
42
+ },
43
+ )
44
+
45
+ logger.info("created_posts_channel", channel_id=entity.id)
46
+ return entity.id
47
+
48
+
49
+ def is_protected_channel(slug: str) -> bool:
50
+ """Check if a channel is protected from deletion.
51
+
52
+ Args:
53
+ slug: Channel slug
54
+
55
+ Returns:
56
+ True if protected
57
+ """
58
+ return slug == DEFAULT_CHANNEL_SLUG
@@ -69,4 +69,26 @@
69
69
  </div>
70
70
  </div>
71
71
  </div>
72
+
73
+ <div class="grid grid-1 mt-4">
74
+ <div class="card">
75
+ <div class="card-header">
76
+ <h3 class="card-title">Focomy バージョン</h3>
77
+ </div>
78
+ <div class="card-body">
79
+ {% if update_info.has_update %}
80
+ <div class="alert alert-warning mb-2">
81
+ <strong>新バージョン {{ update_info.latest_version }} が利用可能です</strong>
82
+ {% if update_info.release_url %}
83
+ <a href="{{ update_info.release_url }}" target="_blank" class="ml-2">リリースノート →</a>
84
+ {% endif %}
85
+ </div>
86
+ <p class="text-muted mb-1">アップデート方法:</p>
87
+ <code>pip install --upgrade focomy</code>
88
+ {% else %}
89
+ <span class="badge badge-success">最新バージョン ({{ update_info.current_version }})</span>
90
+ {% endif %}
91
+ </div>
92
+ </div>
93
+ </div>
72
94
  {% endblock %}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: focomy
3
- Version: 0.1.113
3
+ Version: 0.1.115
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
@@ -7,7 +7,7 @@ core/rate_limit.py,sha256=CX5UjmsU03aFWKXSKjweoHvH2xn0v4NBHNN5ynJC8LE,180
7
7
  core/relations.yaml,sha256=7GUCrphKaouEXNkyd8Ht99e6TcUPERhc4m36RGcc41U,2128
8
8
  core/utils.py,sha256=Rqs1WStB0JjTb8-750jL-xJ_kaPH3ddupvqt46BXIBc,2754
9
9
  core/admin/__init__.py,sha256=IXrr-z-IDXmYodaZ-cVDou6wr_vsVhyWmXHdSNKkQsk,94
10
- core/admin/routes.py,sha256=h9yl5THrlM74ZUPiiFoAlAVN-p3kMR1h-UH_xK1_T0k,147979
10
+ core/admin/routes.py,sha256=BsPienEvO5KOxBObJY-1vPieEvDGqUJsl1XX8shcmy8,149607
11
11
  core/admin/url.py,sha256=FlusKnSz3bZgPSBmRu-dI3W-bQo7lKBDZ3zN8cFHwQc,2243
12
12
  core/api/__init__.py,sha256=H1StbYGDVRS6g-Jk3UUf17ibAz1K8IUa27NfPMkaNrA,19
13
13
  core/api/auth.py,sha256=Zb37IHcUSjf8_hXiVzhoZPQw6WAiOOS_AoMqE96yat8,11565
@@ -83,6 +83,7 @@ core/services/auth.py,sha256=tb_ixUqD0UG2wStilExFxfqTfmW86Rv63svisPDBKCA,19094
83
83
  core/services/block_converter.py,sha256=31iP5TP4eXhar_wF3ojDZd0vmQe9rg5p4QAhDXzyJpg,28110
84
84
  core/services/bulk.py,sha256=xdaLyP7s3mTBV_7t2F5VT5mePNqOvba4MNcnipdyqno,7436
85
85
  core/services/cache.py,sha256=utvAHobZGbF4zaWXPwcB5NIZmClYGT1XXuQjB2C3FFQ,12723
86
+ core/services/channel.py,sha256=s2puzRqSCRLzSvWuKU6q5B1nTN_Ot1dNbo_1JDOUimI,1324
86
87
  core/services/cleanup.py,sha256=kgZCheADrJY21Qn4NAlnh3Li7vEksNDXv8lcot1p6Ew,6686
87
88
  core/services/comment.py,sha256=SOfsFW46vhjw-C050fLim9vXifUogdpYKsEF7tghtz4,8705
88
89
  core/services/config_priority.py,sha256=F4OzreN9WHE69rCTWx_UwgN2DDFj3rU1VK-PTFbaddw,10832
@@ -152,7 +153,7 @@ core/templates/admin/backup.html,sha256=Di93BRvmyxdvF-eOAMKTYFB7qubH1Fms7ppYbJ3Y
152
153
  core/templates/admin/base.html,sha256=yWQNumbtcLIE0iffIoDB2xTF-9W5bt2mdsMYqA5QOa0,23926
153
154
  core/templates/admin/comments.html,sha256=AEzCMYu1-EdFC1yUa9oIObtRS-U9RlMTe_5P3O5alb8,10012
154
155
  core/templates/admin/customize.html,sha256=EwncOC0AgDCSZRum5PZzF-KDYNjrhPish57KMuff4sE,12260
155
- core/templates/admin/dashboard.html,sha256=GsYHM4OYLZWCmleeHrtyS0g8zKhFmlnacJg1LcfGzxM,2513
156
+ core/templates/admin/dashboard.html,sha256=WzURfANu1k9AIjdP82fwJE1o3sRhNVr77iUCRfUoBm8,3439
156
157
  core/templates/admin/entity_form.html,sha256=7LuCyjliuc7bk6S_5Nn4OcXMCfULgZ2z8H48FR88zPo,60261
157
158
  core/templates/admin/entity_list.html,sha256=DxaM2c_1UGdPpQHTIe7LrFxjC11f8hXDy-9C-T8LO6o,7966
158
159
  core/templates/admin/forgot_password.html,sha256=j2r9N5G8T5zKODQu7IVc0NdRvSVuyXBA9FCLn7egp1Q,2320
@@ -201,8 +202,8 @@ themes/minimal/templates/base.html,sha256=LFkx-XLDMGH7oFHHa0e6KPB0DJITOBvr6GtPkD
201
202
  themes/minimal/templates/home.html,sha256=ygYQgYj1OGCiKwmfsxwkPselVKT8vDH3jLLbfphpqKI,1577
202
203
  themes/minimal/templates/page.html,sha256=7Xcoq-ryaxlp913H2S1ishrAro2wsqqGmvsm1osXxd4,389
203
204
  themes/minimal/templates/post.html,sha256=FkTRHci8HNIIi3DU6Mb3oL0aDisGyDcsT_IUDwHmrvo,1387
204
- focomy-0.1.113.dist-info/METADATA,sha256=e8me8ZTyaexIU_y4SvXEnA2Svc1dKHZbbeGALsab2FA,7042
205
- focomy-0.1.113.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
206
- focomy-0.1.113.dist-info/entry_points.txt,sha256=_rF-wxGI1axY7gox3DBsTLHq-JrFKkMCjA65a6b_oqE,41
207
- focomy-0.1.113.dist-info/licenses/LICENSE,sha256=z9Z7gN7NNV7zYCaY-Knh3bv8RBCu89VueYtAlN_-lro,1063
208
- focomy-0.1.113.dist-info/RECORD,,
205
+ focomy-0.1.115.dist-info/METADATA,sha256=bznF_fuygoR7fnXZVdKIgcTa8r5CeWSzqolTzMqC71Y,7042
206
+ focomy-0.1.115.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
207
+ focomy-0.1.115.dist-info/entry_points.txt,sha256=_rF-wxGI1axY7gox3DBsTLHq-JrFKkMCjA65a6b_oqE,41
208
+ focomy-0.1.115.dist-info/licenses/LICENSE,sha256=z9Z7gN7NNV7zYCaY-Knh3bv8RBCu89VueYtAlN_-lro,1063
209
+ focomy-0.1.115.dist-info/RECORD,,