Skip to content
/ elph Public

Elph is a modular and easily customizable content management library that gives you pretty much everything you need for your basic phoenix project in terms of content-management.

License

Notifications You must be signed in to change notification settings

naymspace/elph

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Elph

Elph is a modular and easily customizable content management library that gives you pretty much everything you need for your basic phoenix project in terms of content-management.

It offers you easy content-management and a media library.

Just plug it into your existing phoenix app, add some configs, run migrations and you're ready to go.

NOTE: For the time being elph has no authentication//authorization integrated. This will be added in the future, probably as a plug-in module.

Prerequisites

  • Elph needs ffmpeg to be installed to automatically convert uploaded media-files to browser-friendlier formats. This includes creating thumbnails for images and videos as well as transcoding videos and audio to mp4/mp3.

Usage

  • Add {:elph, "~> 0.9.0} to your mix.exs under deps
  • Run mix deps.get to fetch it.
  • Add the following to your config.exs
config :elph,
  repo: <YourApp>.Repo
  upload_dir: "/app/uploads/"
  url_upload_dir: "/uploads"
  • Add the following to your <YourAppWeb>Endpoint
    • After your first plug Plug.Static-Block
plug Elph.UploadPlug
    • In your plug Plug.Parsers-Block append a length attribute. For example length: 100 * 1024 * 1024 if you want to have a max upload size of 100MB.
  • Add the following supervisor to your <YourApp>.Application in the chilren list in the start/1 function
{Elph.MediaProcessing.BackgroundConverter, name: Elph.MediaProcessing.BackgroundConverter}
  • Copy the migrations from deps/elph/priv/repo/migrations to priv/repo/migrations and run them with mix ecto.create

.Router

To have the most control over everything you can use elph's contexts and controllers and need to do the routing yourself.

  • Add a scope using ElphWeb as ControllerScope and add the Routes using the respective Contollers.
    • Example:
      scope "/api", ElphWeb do
        pipe_through :api
        resources "/contents", ContentController, only: [:index, :show, :create, :delete]
        resources "/media", MediaController, only: [:create]
      end
      
  • Make sure to split those routes and guard them with an authorization mechanism as needed.

FallbackController

Elph has a default phoenix FallbackController to show errors and such. If you want to use your own customized FallbackController add the following to config.exs: config :elph, fallback_controller: <YourAppWeb>.FallbackController

Custom Types

Elph brings with it a list of default types. Those can be found in elph/contents/types. Those are included and available by default without more configuration.

In case this is not enough, you'll need to setup the use of custom types once and adding new types after that is pretty easy.

Setup

First we need to create the central point where we define our custom types.

  • Create a new module like so
defmodule <YourApp>.Contents.Types do
  use Elph.Contents.Types

  elph_types()
end
  • Add this module to the config.exs
config :elph, types: <YourApp>.Contents.Types

In this file you'll later add your types. In case you don't want to add all elph_types() you can use only: [:html, :audio] or except: [:html, :audio] to refine your choice.

Adding Types

Start off with a new schema created by mix phx.gen.schema. For example: mix phx.gen.schema Contents.Types.Markdown markdown_contents markdown:text

  • Hint: As a convention all Elph content types use <type>_contents as their schema source.

Now we need to change some stuff in the <YourApp>.Contents.Types.Markdown module.

  • Add use Ecto.Contents.ContentType below use Ecto.Schema.
  • Change schema "markdown_contents" do to content_schema "markdown_contents" do.
  • Remove the timestamps() call in your schema as Elph already manages timestamps.
  • Write your changeset as you would usually and only care about your own stuff. Don't embed or cast content-variables or child-contents. This will be handled automatically.
    • Care! The function has to be called changeset so it can be called by Elph
  • If you need associated data to be preloaded automatically you can add preloads(action) to your module. This will be passed to Ecto.Repo.preload after your contents have been fetched from the database. Beware: If you have associated elph contents, you don't get whole contents. You'll only get the content type specific data, not the general information (type,name and shared) or the contents' children. This means you can't use the default elph render for those contents. If you need all the fields or even children use content_preloads (see below). Using preloads is faster and you should use it even for contents, if you don't need all fields.
  • If you need all contents fields or even a whole subtree use content_preloads(action). Beware: This works a little different then Ecto.Repo.preload.
    • You can only preload :atom or [:atom1, :atom2]. The other preload syntaxes are not supported. Nested preloads aren't either.
    • While loading the contents their preloads and content_preloads functions will also be loaded. Take care not to create cycles! (In case you do elph will have a fallback limiting the recursion depth to 3 so you don't end in an infinity loop. This can be changed via config option content_preloads_max_depth if your data structs are nested deeper then this)
    • As with Ecto.Repo.preload this will also not preload associations that are already loaded. So make sure not to include your association in both preloads(_) and content_preloads(_).

Add your new type in your central content definition module (See paragraph above).

  • type(:markdown, <YourApp>.<YourContext>.Markdown, <YourAppWeb>.MarkdownView) with the following params
    • The name of your type
    • The newly created module <YourApp>.<YourContext>.MarkdownContent
    • The view which will be called to render the result.
      • It needs a def render("markdown.json", %{content: content}) do function.
        • You can use %{content: content, action: action} instead, if you need to do rendering depending on this information.
        • Defaults are :index and :show.
      • Care! Use <name>.json as first param.

We also need to alter the created migration.

  • Add use Elph.Migration below use Ecto.Migration
  • In the create table call add add_content_field() and remove the timestamps().

Cleanup

If you need your custom types to be cleaned up after them, you can add one or more callbacks.

Per Content-Type (preferred)

Add a def after_delete_callback(content) do to your custom content type module. This callback will be called once for each (explicitly or via garbage collection) deleted content with the deleted content as parameter. If you need the cleanup to re-run afterwards return :cleanup. All other returns are ignored.

Global (Not preferred)

As with custom types you'll first need to create a module:

defmodule <YourApp>.Callbacks do
  use Elph.Contents.Callbacks

  elph_cleanup_callbacks()
end

Now you can add one or more callbacks; for example cleanup_callback(&IO.inspect/1)

Each callback will be called with a list of explicitly deleted and garbage-colledted content (without its children, as one would get from Contents.list_contents). So the above example would print a list of the deleted contents on your console.

If you want to rerun the cleanup after all callbacks were run, return :cleanup in your function. Every other return will be ignored. Care not to produce infinity loops!

Development

For the development of elph we created a project called Elph-Shell. It provides an api with some basic functionality. It also has some configuration set to make developing elph a little easier.

Testing

To test elph you'll need a database. Per default Elph uses a mysql Database and reads the DATABASE_URL environment variable to aquire credentials. In case you don't want to use MySql or a DATABASE_URL you can change your config.ex file. Additionally you'll need to change the adapter in Elph.Test.Repo and import your dependancy via mix.

Preparations

To run tests from within elph-shell you need to create a new database for testing, since using the development database will give you errors. For that open a shell to the docker-container

  • docker-compose enter phoenix bash Login to mysql as root
  • mysql -hdb -uroot -pmysql Create a database and give mysql all permissions
  • CREATE DATABASE elph_test;
  • GRANT ALL PRIVILEGES ON elph_test.* TO 'mysql'@'%';

Running

To run your test simply change to the elph folder in case you're in the shell-directory with cd elph. For the first time - and after changing your migrations - you'll have to run DATABASE_URL=mysql://mysql:mysql@db/elph_test MIX_ENV=test mix ecto.reset.

After that you can run DATABASE_URL=mysql://mysql:mysql@db/elph_test mix test and everything should work out. If you want to see test-coverage you can add the parameter --cover to the command and elixir will show you a coverage percentage and put detailed reports into the cover-subdirectory.

About

Elph is a modular and easily customizable content management library that gives you pretty much everything you need for your basic phoenix project in terms of content-management.

Resources

License

Stars

Watchers

Forks