Module: RivetCms
- Defined in:
- lib/rivet_cms/seeds.rb,
lib/rivet_cms.rb,
lib/rivet_cms/audit.rb,
lib/rivet_cms/hooks.rb,
lib/rivet_cms/engine.rb,
lib/rivet_cms/version.rb,
lib/rivet_cms/navigation.rb,
app/models/rivet_cms/user.rb,
app/models/rivet_cms/entry.rb,
app/models/rivet_cms/field.rb,
lib/rivet_cms/access_check.rb,
lib/rivet_cms/safe_pattern.rb,
app/models/rivet_cms/current.rb,
app/models/rivet_cms/category.rb,
app/models/rivet_cms/document.rb,
app/models/rivet_cms/relation.rb,
app/models/rivet_cms/api_token.rb,
app/models/rivet_cms/component.rb,
app/services/rivet_cms/webhooks.rb,
app/models/rivet_cms/media_asset.rb,
app/models/rivet_cms/content_type.rb,
app/models/rivet_cms/organization.rb,
app/jobs/rivet_cms/application_job.rb,
app/models/rivet_cms/content_value.rb,
app/services/rivet_cms/draft_writer.rb,
app/services/rivet_cms/content_query.rb,
app/models/rivet_cms/entry_collection.rb,
app/helpers/rivet_cms/dashboard_helper.rb,
app/models/rivet_cms/document_revision.rb,
app/services/rivet_cms/revision_pruner.rb,
app/helpers/rivet_cms/components_helper.rb,
app/jobs/rivet_cms/webhook_delivery_job.rb,
app/models/concerns/rivet_cms/sluggable.rb,
app/models/rivet_cms/application_record.rb,
app/models/rivet_cms/component_instance.rb,
app/helpers/rivet_cms/application_helper.rb,
app/mailers/rivet_cms/application_mailer.rb,
app/models/concerns/rivet_cms/has_fields.rb,
app/services/rivet_cms/content_validator.rb,
app/controllers/rivet_cms/auth_controller.rb,
app/models/concerns/rivet_cms/typed_value.rb,
app/services/rivet_cms/open_api_generator.rb,
app/controllers/rivet_cms/setup_controller.rb,
app/controllers/rivet_cms/trash_controller.rb,
app/controllers/rivet_cms/users_controller.rb,
app/helpers/rivet_cms/content_types_helper.rb,
app/controllers/rivet_cms/fields_controller.rb,
app/controllers/rivet_cms/content_controller.rb,
app/models/concerns/rivet_cms/revision_owned.rb,
app/models/concerns/rivet_cms/soft_deletable.rb,
app/serializers/rivet_cms/revision_preloader.rb,
app/controllers/rivet_cms/api_docs_controller.rb,
app/controllers/rivet_cms/sessions_controller.rb,
app/serializers/rivet_cms/revision_serializer.rb,
app/controllers/rivet_cms/dashboard_controller.rb,
app/controllers/rivet_cms/documents_controller.rb,
app/controllers/rivet_cms/api_tokens_controller.rb,
app/controllers/rivet_cms/components_controller.rb,
app/controllers/concerns/rivet_cms/inertia_props.rb,
app/controllers/rivet_cms/application_controller.rb,
app/controllers/rivet_cms/invitations_controller.rb,
app/controllers/rivet_cms/media_assets_controller.rb,
app/models/concerns/rivet_cms/organization_scoped.rb,
app/controllers/rivet_cms/content_types_controller.rb,
lib/generators/rivet_cms/install/install_generator.rb,
app/controllers/rivet_cms/content_manager_controller.rb
Overview
Content-type templates: reusable schema (content types, fields, components, categories) that a host app can load into an organization as a starting point. Templates live in db/seeds/templates and are applied idempotently, so loading the same template twice upserts rather than duplicating — safe in production.
Defined Under Namespace
Modules: ApplicationHelper, Audit, ComponentsHelper, ContentTypesHelper, DashboardHelper, Generators, HasFields, Hooks, InertiaProps, Navigation, OrganizationScoped, RevisionOwned, SafePattern, Seeds, Sluggable, SoftDeletable, TypedValue, Webhooks Classes: AccessCheck, AccessDenied, ApiDocsController, ApiToken, ApiTokensController, ApplicationController, ApplicationJob, ApplicationMailer, ApplicationRecord, AuditEvent, AuthController, Category, Component, ComponentInstance, ComponentsController, ContentController, ContentInvalidError, ContentManagerController, ContentQuery, ContentType, ContentTypesController, ContentValidator, ContentValue, Current, DashboardController, Document, DocumentRevision, DocumentsController, DraftWriter, Engine, Entry, EntryCollection, Field, FieldsController, InvitationsController, MediaAsset, MediaAssetsController, OpenApiGenerator, Organization, Relation, RemovedContentTypeError, RevisionImmutableError, RevisionPreloader, RevisionPruner, RevisionSerializer, SessionsController, SetupController, TrashController, TrashedEntryError, User, UsersController, WebhookDeliveryJob
Constant Summary collapse
- DEFAULT_AUTHENTICATE =
authenticate returns truthy to allow the request and falsy to deny it; the engine turns a denial into a redirect to login_path or a 403 (a lambda may also render/redirect itself). Unconfigured means built-in authentication: this lambda is an identity sentinel for builtin_auth? and is never called (the controller branches to the built-in session flow first). Fails closed if something ever invokes it anyway.
->(_controller) { false }
- VERSION =
"0.3.1"
Class Attribute Summary collapse
- .admin_scripts ⇒ Object
- .admin_stylesheets ⇒ Object
-
.allowed_media_types ⇒ Object
MIME types the media library accepts, checked against the sniffed type, not the client-declared one.
-
.authenticate ⇒ Object
Returns the value of attribute authenticate.
-
.can ⇒ Object
Authorization seam: receives one RivetCms::AccessCheck and returns a boolean; default allow.
-
.current_user ⇒ Object
Returns the value of attribute current_user.
-
.login_path ⇒ Object
Returns the value of attribute login_path.
-
.logout_method ⇒ Object
Returns the value of attribute logout_method.
-
.logout_path ⇒ Object
Returns the value of attribute logout_path.
-
.max_upload_size ⇒ Object
Hard ceiling for library uploads (bytes); hosts can override in an initializer.
-
.media_host ⇒ Object
Host (e.g. "https://cms.example.com") used to build absolute media URLs in the public API.
-
.parent_controller ⇒ Object
Authentication is delegated to the host app.
-
.public_api ⇒ Object
When true the delivery API allows anonymous reads of published content.
-
.revision_retention ⇒ Object
How many superseded published revisions to keep per document.
-
.setup_code ⇒ Object
Derived, not memoized: memoizing would make the explicit-config check below true after any read.
-
.user_email ⇒ Object
Returns the value of attribute user_email.
-
.user_name ⇒ Object
Returns the value of attribute user_name.
-
.webhooks ⇒ Object
Basic webhook endpoints: [{ url: "https://...", events: %w[entry.published] }].
Class Method Summary collapse
-
.asset_version ⇒ Object
Digest of the precompiled admin assets, used as the Inertia asset version so clients do a full reload when the gem ships a new build.
-
.builtin_auth? ⇒ Boolean
Built-in authentication is the default: it is active exactly while the host has not configured its own authenticate lambda.
- .compute_asset_version ⇒ Object
- .configure {|_self| ... } ⇒ Object
-
.entries(type_slug, organization: nil, **options) ⇒ Object
Published entries of a content type.
-
.entry(type_slug, entry_slug, organization: nil, preview: false, populate: nil, fields: nil) ⇒ Object
One entry by slug, or nil.
-
.normalized_retention_for(document) ⇒ Object
What the pruner actually reads: an override is validated the same way the config setter is, so a bad override cannot silently destroy history.
-
.on(event, callable = nil, key: nil, &block) ⇒ Object
Subscribe to a lifecycle event; see RivetCms::Hooks for the event list and the key: contract for reload-safe registration.
-
.register_admin_script(name) ⇒ Object
Extra admin bundles the layout emits after core's tags.
- .register_admin_stylesheet(name) ⇒ Object
-
.register_nav(key, **options) ⇒ Object
Add a sidebar item; see RivetCms::Navigation for the full contract.
-
.retention_for(_document) ⇒ Object
Retention for one document.
- .setup_code_required? ⇒ Boolean
-
.single(type_slug, organization: nil, preview: false, populate: nil, fields: nil) ⇒ Object
The one entry of a single-type content type, or nil.
Class Attribute Details
.admin_scripts ⇒ Object
54 55 56 |
# File 'lib/rivet_cms.rb', line 54 def admin_scripts @admin_scripts ||= [] end |
.admin_stylesheets ⇒ Object
58 59 60 |
# File 'lib/rivet_cms.rb', line 58 def admin_stylesheets @admin_stylesheets ||= [] end |
.allowed_media_types ⇒ Object
MIME types the media library accepts, checked against the sniffed type, not the client-declared one. nil disables the check. SVG is excluded by default because scripted SVGs are an XSS vector; hosts that trust their editors can append "image/svg+xml".
23 24 25 |
# File 'lib/rivet_cms.rb', line 23 def allowed_media_types @allowed_media_types end |
.authenticate ⇒ Object
Returns the value of attribute authenticate.
113 114 115 |
# File 'lib/rivet_cms.rb', line 113 def authenticate @authenticate end |
.can ⇒ Object
Authorization seam: receives one RivetCms::AccessCheck and returns a boolean; default allow. check.action is :read, :write, :publish, or :delete; check.resource is a coarse domain (:content, :schema, :media, :api). The vocabulary grows over time, so policies should allowlist known pairs and deny anything unrecognized. A raising policy denies (fail closed) and logs. Governs the admin UI only; the delivery API is token-gated separately.
108 109 110 |
# File 'lib/rivet_cms.rb', line 108 def can @can end |
.current_user ⇒ Object
Returns the value of attribute current_user.
114 115 116 |
# File 'lib/rivet_cms.rb', line 114 def current_user @current_user end |
.login_path ⇒ Object
Returns the value of attribute login_path.
117 118 119 |
# File 'lib/rivet_cms.rb', line 117 def login_path @login_path end |
.logout_method ⇒ Object
Returns the value of attribute logout_method.
119 120 121 |
# File 'lib/rivet_cms.rb', line 119 def logout_method @logout_method end |
.logout_path ⇒ Object
Returns the value of attribute logout_path.
118 119 120 |
# File 'lib/rivet_cms.rb', line 118 def logout_path @logout_path end |
.max_upload_size ⇒ Object
Hard ceiling for library uploads (bytes); hosts can override in an initializer.
17 18 19 |
# File 'lib/rivet_cms.rb', line 17 def max_upload_size @max_upload_size end |
.media_host ⇒ Object
Host (e.g. "https://cms.example.com") used to build absolute media URLs in the public API. When nil, URLs are relative paths.
27 28 29 |
# File 'lib/rivet_cms.rb', line 27 def media_host @media_host end |
.parent_controller ⇒ Object
Authentication is delegated to the host app. See the initializer template (rails g rivet_cms:install) for a full example.
112 113 114 |
# File 'lib/rivet_cms.rb', line 112 def parent_controller @parent_controller end |
.public_api ⇒ Object
When true the delivery API allows anonymous reads of published content. When false (default) every request needs an API token. A preview-scoped token is always required to read drafts, regardless of this setting.
74 75 76 |
# File 'lib/rivet_cms.rb', line 74 def public_api @public_api end |
.revision_retention ⇒ Object
How many superseded published revisions to keep per document. The default :all never deletes anything: pruning is something a host opts into, because unbounded storage is a visible problem you can fix later and deleted content is not. Set an integer to prune on publish. The current published revision and the working draft are never pruned.
81 82 83 |
# File 'lib/rivet_cms.rb', line 81 def revision_retention @revision_retention end |
.setup_code ⇒ Object
Derived, not memoized: memoizing would make the explicit-config check below true after any read
177 178 179 |
# File 'lib/rivet_cms.rb', line 177 def setup_code @setup_code || Digest::SHA256.hexdigest("#{Rails.application.secret_key_base}:rivet-cms-setup")[0, 12] end |
.user_email ⇒ Object
Returns the value of attribute user_email.
116 117 118 |
# File 'lib/rivet_cms.rb', line 116 def user_email @user_email end |
.user_name ⇒ Object
Returns the value of attribute user_name.
115 116 117 |
# File 'lib/rivet_cms.rb', line 115 def user_name @user_name end |
.webhooks ⇒ Object
Basic webhook endpoints: [{ url: "https://...", events: %w[entry.published] }]. events is optional (defaults to all). Delivered by WebhookDeliveryJob; no signing or retries.
32 33 34 |
# File 'lib/rivet_cms.rb', line 32 def webhooks @webhooks end |
Class Method Details
.asset_version ⇒ Object
Digest of the precompiled admin assets, used as the Inertia asset version so clients do a full reload when the gem ships a new build. Recomputed each request in development so a rebuild auto-reloads the browser; memoized elsewhere (assets are static at runtime).
289 290 291 292 293 |
# File 'lib/rivet_cms.rb', line 289 def self.asset_version return compute_asset_version if Rails.env.development? @asset_version ||= compute_asset_version end |
.builtin_auth? ⇒ Boolean
Built-in authentication is the default: it is active exactly while the host has not configured its own authenticate lambda. Setting one (as every existing host integration does) opts out entirely.
165 166 167 |
# File 'lib/rivet_cms.rb', line 165 def builtin_auth? authenticate.equal?(DEFAULT_AUTHENTICATE) end |
.compute_asset_version ⇒ Object
295 296 297 298 299 300 301 302 303 |
# File 'lib/rivet_cms.rb', line 295 def self.compute_asset_version build_path = Engine.root.join("app/assets/builds") asset_digests = %w[rivet_cms.js rivet_cms.css].filter_map do |filename| asset = build_path.join(filename) Digest::MD5.file(asset).hexdigest if asset.exist? end asset_digests.any? ? Digest::MD5.hexdigest([ VERSION, *asset_digests ].join(":")) : VERSION end |
.configure {|_self| ... } ⇒ Object
121 122 123 |
# File 'lib/rivet_cms.rb', line 121 def configure yield self end |
.entries(type_slug, organization: nil, **options) ⇒ Object
Published entries of a content type. Options mirror the API's list params; populate accepts an array of keys or :all.
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/rivet_cms.rb', line 131 def entries(type_slug, organization: nil, **) content_type = find_content_type!(type_slug, organization) query = ContentQuery.new(content_type, **) populate = query.populate_fields page = query.documents revisions = page.map(&:published_revision) preload = RevisionPreloader.new(revisions, populate_fields: populate) wrapped = revisions.map do |revision| Entry.new(RevisionSerializer.new(revision, fields: query.field_keys, populate: populate, preload: preload).as_json) end EntryCollection.new(wrapped, page: page.current_page, per_page: page.limit_value, total: page.total_count, total_pages: page.total_pages) end |
.entry(type_slug, entry_slug, organization: nil, preview: false, populate: nil, fields: nil) ⇒ Object
One entry by slug, or nil. preview: true serves the draft when present (Ruby callers are trusted host code — no token gate).
149 150 151 152 153 |
# File 'lib/rivet_cms.rb', line 149 def entry(type_slug, entry_slug, organization: nil, preview: false, populate: nil, fields: nil) content_type = find_content_type!(type_slug, organization) document = content_type.documents.find_by(slug: entry_slug) serialize_document(content_type, document, preview: preview, populate: populate, fields: fields) end |
.normalized_retention_for(document) ⇒ Object
What the pruner actually reads: an override is validated the same way the config setter is, so a bad override cannot silently destroy history.
97 98 99 |
# File 'lib/rivet_cms.rb', line 97 def normalized_retention_for(document) normalize_retention(retention_for(document)) end |
.on(event, callable = nil, key: nil, &block) ⇒ Object
Subscribe to a lifecycle event; see RivetCms::Hooks for the event list and the key: contract for reload-safe registration. RivetCms.on(:publish) { |revision| ... }
67 68 69 |
# File 'lib/rivet_cms.rb', line 67 def on(event, callable = nil, key: nil, &block) Hooks.on(event, callable, key: key, &block) end |
.register_admin_script(name) ⇒ Object
Extra admin bundles the layout emits after core's tags. An extension's precompiled JS loads in order and can register its pages on window.RivetCMS before the admin app boots.
42 43 44 45 46 |
# File 'lib/rivet_cms.rb', line 42 def register_admin_script(name) raise ArgumentError, "asset name required" if name.to_s.strip.empty? admin_scripts << name.to_s unless admin_scripts.include?(name.to_s) end |
.register_admin_stylesheet(name) ⇒ Object
48 49 50 51 52 |
# File 'lib/rivet_cms.rb', line 48 def register_admin_stylesheet(name) raise ArgumentError, "asset name required" if name.to_s.strip.empty? admin_stylesheets << name.to_s unless admin_stylesheets.include?(name.to_s) end |
.register_nav(key, **options) ⇒ Object
Add a sidebar item; see RivetCms::Navigation for the full contract.
35 36 37 |
# File 'lib/rivet_cms.rb', line 35 def register_nav(key, **) Navigation.register(key, **) end |
.retention_for(_document) ⇒ Object
Retention for one document. Override to vary by organization or content type; the scalar config is the default resolver.
91 92 93 |
# File 'lib/rivet_cms.rb', line 91 def retention_for(_document) revision_retention end |
.setup_code_required? ⇒ Boolean
181 182 183 |
# File 'lib/rivet_cms.rb', line 181 def setup_code_required? !(Rails.env.development? || Rails.env.test?) || @setup_code.present? end |
.single(type_slug, organization: nil, preview: false, populate: nil, fields: nil) ⇒ Object
The one entry of a single-type content type, or nil.
156 157 158 159 160 |
# File 'lib/rivet_cms.rb', line 156 def single(type_slug, organization: nil, preview: false, populate: nil, fields: nil) content_type = find_content_type!(type_slug, organization) document = content_type.documents.find_by(singleton_key: "singleton") || content_type.documents.first serialize_document(content_type, document, preview: preview, populate: populate, fields: fields) end |