LogoLogo
v2.2.0
v2.2.0
  • 🌸What is Petal Pro?
  • πŸ’‘Changelog
  • ⏫Upgrade guide
  • Guides
    • πŸš€Creating a web app from start to finish
    • 🌎Deploy to Fly.io
    • πŸ’³Adding a subscription
    • πŸ”’Creating Your Own API
    • πŸ”ŒContent Editor - adding your own plug-in
  • πŸ‘©β€πŸ³Recipes
    • πŸ’How to apply a recipe with git cherry pick
    • #️⃣UUIDs
    • ✍️First/Last name
    • πŸ—ΊοΈGoogle Maps
    • Password Hashing for Windows
  • Fundamentals
    • πŸ’ΏInstallation
    • πŸ“‚Folder structure
    • πŸ—ƒοΈIncluded Pages
    • πŸ˜€Users & Authentication
    • 🏒Organizations & Multitenancy
    • πŸ’³Stripe billing
    • Blog/CMS
    • πŸ””User Notifications
    • 🧊Components
    • ⬛Dark mode
    • 🎨Branding
    • 🌱Seeding
    • πŸ“„Layouts & Menus
    • πŸ–ΌοΈImage uploads
    • πŸ‘₯Impersonation
    • πŸ› οΈBackground Tasks and Jobs
    • πŸ”§Util & Helpers
    • πŸ“§Emails
    • πŸͺJavascript Hooks
    • πŸ“šExtra Hex Libraries
    • πŸ—οΈGenerators
    • πŸ—£οΈTranslations
    • πŸ–οΈContributing
    • πŸ›«Deployment
    • πŸ›‘οΈTesting
    • πŸ”’REST API
Powered by GitBook
On this page
  • Data Entry
  • Publishing Process
  • Un-publishing
  • Content Editor
  • Pre-installed Editor.js Plug-ins
  • Adding Plug-ins
  • Data Structure
  • File Browser

Was this helpful?

  1. Fundamentals

Blog/CMS

Blogging system based on the Content Editor component.

PreviousStripe billingNextUser Notifications

Last updated 5 months ago

Was this helpful?

The Content Management System (CMS) provides a basic blogging platform. The CMS provides a set of basic features:

  • Minimal data structure - one table for Posts, one table for Files

  • Simple publishing process - draft fields are copied over published fields

  • Rich content is supported via the Content Editor (integrated with

  • File browser allows the user to upload and select files

  • Admin console to manage blog posts

  • Basic UI to show published posts

Data Entry

To create a blog post, go to the admin console:

/admin/posts

Here you'll see the list of posts:

To create a post, click the "New Post" button:

Fill in the fields (only Title is required) and click on "Save and Continue". Here you'll see the main data entry screen:

While on the edit page, any changes made to the document will be auto-saved!

Publishing Process

Once your happy with your edits, click on "Save and Finish". This will bring you to a preview of the draft:

To make this post publically available, click on the "Publish" button:

To complete the process, select a "Go Live" date/time and hit the second "Publish" button!

The Go Live date/time is based on UTC. Currently there is no support for user/timezone configuration in Petal Pro. If the time of day matters, you'll need to convert it to UTC before filling out this form.

Un-publishing

Once a post has been published, you can bring up the Publish modal again:

To un-publish, click the "Remove" button. This won't affect data entry, but it will ensure that the post is no longer publically available via the Blog.

Content Editor

pic

Pre-installed Editor.js Plug-ins

Editor.js has it's own eco-system - functionality is provided via plug-ins. The following plug-ins are installed by default:

Finally, we've created a plug-in that integrates with the file browser, called PetalImage

Adding Plug-ins

The only requirement is that the Content Editor component is updated so that the renderer can output data from the new plug-in. Thankfully, this is extremely easy to do. In fact, here's a tutorial on how you can create your own Editor.js plug-in and adjust the Content Editor:

Data Structure

Here's what a Post looks like:

schema "posts" do
  # Draft fields (used for editing)
  field :category, :string
  field :title, :string
  field :slug, :string
  field :cover, :string
  field :cover_caption, :string
  field :summary, :string
  field :content, :string
  field :duration, :string

  # Published fields
  field :published_category, :string
  field :published_title, :string
  field :published_slug, :string
  field :published_cover, :string
  field :published_cover_caption, :string
  field :published_summary, :string
  field :published_content, :string
  field :published_duration, :string

  # The last time this post was published
  field :last_published, :naive_datetime
  
  # Date/time when post is publically available. 
  # `nil` means "private" or "not published"
  field :go_live, :utc_datetime

  belongs_to :author, PetalPro.Accounts.User

  timestamps(type: :utc_datetime)
end

Blog posts belong to a user. However, the default behaviour is that only admins have access to the console.

As a general rule, the draft fields are used in the admin console. The published fields are only used in the public facing Blog.

At the time of saving a draft, if the :title has changed, then it is used to generate :slug. The :slug includes the :id as a postfix (base64 encoded). So if the title is, "Hello Fred", then the slug will look similar to hello-fred-OwAw1rxK. This way it won't matter if the :title changes, pasting an old :slug into the browser will still result in loading the correct Blog post.

When publishing a post, the draft fields are copied over the published fields. In addition :last_published is set to DateTime.now_utc and :go_live is based on the user's data entry.

The cover field is a url to an image (and by extension published_cover is too). This can be any url, but you'll need to make sure the domain is configured in the Content Security Policy. To find out how to configure the Content Security Policy (or disable it), check out:

Here's what the File data structure looks like:

schema "files" do
  field :url, :string
  field :name, :string
  
  # Files aren't removed, they're archived
  field :archived, :boolean, default: false

  belongs_to :author, PetalPro.Accounts.User

  timestamps(type: :utc_datetime)
end

Again, a file belongs to a user, but by default only admins have access to the console.

A file points to a url and has a name. Files are intended to be single use - you can add a file, name it and then you're done. If you want something different, you add a new file. If you want to replace a file, archive the old file and add a new one. This behaviour is supported by the File Browser. The main benefit of this design is that you can control what you see, but limit the chance of accidentally deleting a file that's in use.

File Browser

The file browser provides a means to upload and select images:

defmodule PetalProWeb.AdminFilesLive.FormComponent do
  @moduledoc false
  use PetalProWeb, :live_component

  alias PetalPro.Files
  alias PetalPro.Files.File
  alias PetalProWeb.FileUploadComponents

  @upload_provider PetalPro.FileUploads.Local
  # @upload_provider PetalPro.FileUploads.Cloudinary
  # @upload_provider PetalPro.FileUploads.S3

  @impl true
  def update(assigns, socket) do
    socket =
      socket
      |> assign(assigns)
      |> assign_new(:form, fn -> %File{} |> Files.change_file() |> to_form() end)
      |> assign(:uploaded_files, [])
      |> allow_upload(:new_file,
        # SETUP_TODO: Uncomment the line below if using an external provider (Cloudinary or S3)
        # external: &@upload_provider.presign_upload/2,
        accept: ~w(.jpg .jpeg .png .gif .svg .webp),
        max_entries: 1
      )

    {:ok, socket}
  end
  
  # ...
end

As you can see, this can be configured to work with an external uploader instead. You can use one of the built-in providers (Cloudinary or S3).

In the case of a local server upload, Cloudinary or Amazon S3 - the Content Security Policy has been pre-configured and should work out of the box.

You can click the cover image to bring up the . The main data entry starts at the section that says, "Start typing...". This part of the document is using the .

Draft data will be copied over published fields. For more details on what those fields are and what else happens, see the section. Assuming the post is live, it will be available in the Blog.

The Content Editor is based on . You can use it to create paragraphs, lists, tables, insert images and even insert embeds.

Editor.js is a block-style editor that that generates a json document as output. This json is captured and stored in the Post data structure (see below for more details). Rendering is taken care of by the Content Editor , via the .content and .pretty_content function components.

(h1 to h6)

(similar to a markdown quote)

(a tool for highlighting sections of text)

(so you can display inline text as code)

(so you can show a code block)

(break up paragraphs with a line-based delimeter)

(ordered and unordered lists)

(paste in a url to an image and it will render)

(UI for creating a HTML table)

(displays an alert, similar to the next section)

(e.g. paste a YouTube link to create an embedded iframe)

The SimpleImage plug-in will only work for urls that are configured in the Content Security Policy. See the section for more information on the Content Security Policy

There are many core and community-based plug-ins available for Editor.js and there's nothing stopping you from using them! Petal Pro uses npm for installation. Documentation for installing plug-ins can be found .

The output of the Content Editor component (i.e. Editor.js json ) is stored in the content field (and published to the published_content field).

Though the cover image can point to any url, in reality the domain will probably be defined by the file browser. See the section (below) for more information.

NB - the file browser is currently targetted at images (i.e. files that are easy to preview). Other file types (such as pdf documents) could be supported but will require more work to enable. If you're interested in a feature like this, please consider adding it to the Petal Pro .

Based on LiveView the file browser defaults to storing files on the local server:

component
Editor.js
Header
Quote
Marker
InlineCode
Code
Delimiter
List
SimpleImage
Table
Warning
Embeds
here
πŸ”ŒContent Editor - adding your own plug-in
data structure
πŸ›«Deployment
roadmap
Uploads
file browser
Content Editor
Data Structure
component
Data Structure
Data Structure
File Browser
component
Editor.js)
Posts admin console
New Post modal
Main data entry screen for a post
Preview of the current draft
Publish modal
Remove button is enabled for published post
Upload or select image