Single source of truth for Birkly CMS documentation

Canonical model for posts, comments, likes, and profiles: one owner field per schema, a plugin-private social graph, and platform-enforced session ownership on public APIs.

Requires User Management 1.0.42+ (graph fieldtype + private store) and Birkly core with entry_ownership.php, virtual fieldtype hooks, and public-read filters.

UGC collections declare a single type: "user" field with ownership: true. Public create paths (public_submit, public_sub_entry) set owner from the logged-in session — clients cannot spoof it. Social edges (follow, connection, block) live in plugin-private storage (data/plugins/user_management/graph/) via UserGraphService and the user_graph API. Profile collections may add a user_relationship virtual field for the admin relationship panel — graph data is not stored on the profile entry JSON. Reactions (likes, votes) are sub-entries with their own owner field, not plugin metadata.

Beginner

Three primitives

PrimitiveWherePurpose
Owner fieldEntry / sub-entry schemaLinks content to an account (owner, type: user, ownership: true)
Profile entryConfigurable profile collectionPublic card for an account (avatar, bio) + optional user_relationship admin panel
User graphPlugin-private graph/ storeDirected edges: follow (one-way), connection (mutual), block

Relationship kinds

KindMeaning
FollowOne-way — A follows B
ConnectionMutual — request, accept, active
BlockOne-way safety edge

The legacy user-relationships CMS collection is migrated and hidden on upgrade to 1.0.42+.

Schema example (post collection)

{
  "name": "owner",
  "type": "user",
  "label": "Author",
  "ownership": true,
  "required": true,
  "public_visible": false
}

In the collection editor, enable Entry owner on a User field. Owner fields should stay hidden on public forms (public_visible: false).

Public create behavior

SurfaceOwner
Logged-in visitor submits post/comment/likeSet from session automatically
Anonymous submit to owner-required collectionRejected (401)
Client sends owner in POST bodyIgnored — platform overwrites

Optional author_name text may remain as a display snapshot; do not use text author_user_id / user_id for permissions.

Social graph API

Use user_graph (not legacy club_social or user_relations):

ActionMethodNotes
follow / unfollowPOSTDirected active follow edge
connect / accept / reject / disconnectPOSTMutual connections
block / unblockPOSTBlocks follow/connect
stateGETfollowing, followers, connected, pending, blocked

Site members use user_graph as themselves. Admins editing a profile entry use user_graph_admin with subject_user_id (inline from the user_relationship fieldtype panel).

Likes and votes

Toggle via public_sub_entry.php with operation: toggle on a likes sub-collection. Each like is a sub-entry with owner = session user. Roadmap votes use the same pattern.

Advanced Users

Template functions

FunctionPurpose
{user_current}Current account
{user_profile entry.owner}Profile for entry owner
{user_is_following from to}Boolean follow check
{user_is_friend a b}Alias for connected / legacy friend check
{user_is_connected a b}Active mutual connection
{user_is_blocked a b}Block edge
{user_graph_follows user_id}User IDs this account follows
{user_graph_followers user_id}Follower user IDs
{user_graph_connected user_id}Connected user IDs
{user_can_view entry}EntryPermissions visibility

Entry visibility

EntryPermissions enforces public, private, relationship, and tier on list and get when the User plugin registers birkly_public_entries_filter / birkly_public_entry_filter hooks (declared in plugin.json).

Migration

On plugin upgrade:

  • scripts/migrate-p75-ugc.php — legacy author fields, reaction metadata, follow metadata
  • scripts/migrate-p76-graph-store.php (1.0.42+) — user-relationships collection edges → plugin graph store; hides legacy collection

Re-run safely after data fixes: upgrade to latest User plugin or run scripts from CLI on the CMS host.

Validation

Birkly core rejects collection schemas with more than one ownership: true field, more than one user_relationship field, or user_relationship without an owner field (admin save + MCP update_collection). Virtual fieldtypes do not persist on entry JSON.