{"id":25417370,"url":"https://github.com/martide/literature","last_synced_at":"2025-10-31T09:31:14.472Z","repository":{"id":58198811,"uuid":"526870106","full_name":"martide/literature","owner":"martide","description":null,"archived":false,"fork":false,"pushed_at":"2025-02-14T08:47:55.000Z","size":25518,"stargazers_count":9,"open_issues_count":2,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-02-16T12:16:21.915Z","etag":null,"topics":["cms","elixir","liveview","phoenix"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/martide.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-20T08:34:33.000Z","updated_at":"2025-02-12T15:34:33.000Z","dependencies_parsed_at":"2023-11-12T13:29:12.647Z","dependency_job_id":"9808eb37-c04f-49cf-9be1-42a86dae5c10","html_url":"https://github.com/martide/literature","commit_stats":{"total_commits":236,"total_committers":7,"mean_commits":"33.714285714285715","dds":0.6186440677966102,"last_synced_commit":"feee7403feac1fd4821f666249dd2f03f8d11ec2"},"previous_names":[],"tags_count":69,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martide%2Fliterature","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martide%2Fliterature/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martide%2Fliterature/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/martide%2Fliterature/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/martide","download_url":"https://codeload.github.com/martide/literature/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239126542,"owners_count":19586103,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cms","elixir","liveview","phoenix"],"created_at":"2025-02-16T17:38:50.087Z","updated_at":"2025-10-31T09:31:14.463Z","avatar_url":"https://github.com/martide.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Literature\n\n`Literature` is a blog content management system with core blog features such as `Publications`, `Posts`, `Authors`, and `Tags`. Content can be published with static html files through a static pages generator that uses `Phoenix.Components`. It comes with a WYSIWYG post editor, image optimization, reusable SEO tags, and seamless integration to an existing Phoenix application.\n\n## Installation\n\n```elixir\ndef deps do\n  [\n    {:literature, \"~\u003e 0.4\"}\n  ]\nend\n```\n\n## Quick Setup\n\nLiterature comes with built in database tables that can be added through a migration.\n\n1. Setup config including Ecto repo for Literature to use and directory where static pages will be stored.\n\n```elixir\nconfig :literature,\n  repo: MyApp.Repo,\n  static_pages_storage_dir: \"/tmp/literature/static_pages\"\n```\n\n2. Setup Literature database tables by creating a new migration. This will create all necessary tables `Publication`, `Post`, `Author`, and `Tag`.\n\n```elixir\ndefmodule MyApp.Repo.Migrations.SetupLiterature do\n  use Ecto.Migration\n\n  def up, do: Literature.Migrations.up([])\n  def down, do: Literature.Migrations.down([])\nend\n```\n\n3. Setup content management dashboard in your router. Use `Literature.Router` and add `literature_assets/1` for styling and JS files and `literature_dashboard/1` for the dashboard LiveView pages.\n\n```elixir\ndefmodule MyAppWeb.Router do\n  ...\n  use Literature.Router\n\n  literature_assets(\"/literature\")\n  literature_dashboard(\"/literature\")\nend\n```\n\n4. You can separate content by main topic by creating a publication for each. Create a publication e.g. `Blog` then create authors and tags. You can now start creating Posts with a WYSIWYG editor.\n\n## Images\n\nLiterature tables have a few image columns such as feature and profile images. Post content could also have images, both are handled with\n\n- [Waffle](https://github.com/elixir-waffle/waffle) - for uploading to storage buckets\n- [Image](https://github.com/elixir-image/image) - for image processing and transforms\n\nSetup literature config for waffle, e.g. for local config\n\n```elixir\nconfig :literature,\n  storage: Waffle.Storage.Local,\n  storage_dir_prefix: \"/tmp/literature/\",\n  asset_host: \"/tmp/literature\"\n```\n\n## WSYIWYG Post content editor\n\nPost editor supports content formatting and is built with [Milkdown.js](https://milkdown.dev/) markdown editor.\nContent is saved both as markdown, and also converted to html for easy rendering with image tags converted to responsive images.\nCurrently supported features for the editor are `Headings`, `Images`, `Lists`, `Blockquote`, `Tables`, and various formatting such as setting hyperlinks, bold, italic, and underline.\n\n![editor screenshot](images/editor.png)\n\nCurrent rendered styling in the editor is styled with [Tailwind Typography prose](https://github.com/tailwindlabs/tailwindcss-typography). Content styling can be customized on the templates it will be used on and can be rendered through [Phoenix.HTML.raw/1](https://hexdocs.pm/phoenix_html/Phoenix.HTML.html#raw/1)\n\n```elixir\n\u003cdiv class=\"prose\"\u003e\n  \u003c%= raw(post.html) %\u003e\n\u003c/div\u003e\n```\n\n## Generating Static pages\n\n`Literature.StaticPages.Generator`, `Literature.StaticPages.Layout`, and `Literature.StaticPages.Templates` will be used to generate your static HTML files.\n\n- `Literature.StaticPages.Layout` - Behaviour for static pages layout. Provides a built in `layout/1` component that can be used readily with SEO tags.\n- `Literature.StaticPages.Templates` - Behaviour that should contain all templates that will be used for static page generation. Provides placeholder components for all page types. See Available Pages for all available page types.\n- `Literature.StaticPages.Generator` - contains all functions that will write your static files, either to a file or in memory.\n\n1. Create your templates module using the built in layout. Here you can fully customize the content of your page and add more assigns using `Phoenix.Components`.\n\n```elixir\ndefmodule MyAppWeb.Blog.Templates do\n  use Phoenix.Component\n  @behavior Literature.StaticPages.Templates\n\n  import Literature.StaticPages.Layout, only: [layout: 1]\n\n  @impl true\n  def index(assigns) do\n    ~H\"\"\"\n    \u003c.layout {assigns}\u003e\n      \u003ch1\u003e{@publication.name}\u003c/h1\u003e\n      \u003ch2\u003ePosts\u003c/h2\u003e\n      \u003cul\u003e\n        \u003cli :for={post \u003c- @posts}\u003e\n          {post.title}\n        \u003c/li\u003e\n      \u003c/ul\u003e\n    \u003c/.layout\u003e\n    \"\"\"\n  end\n\n  def show(assigns) do\n    ~H\"\"\"\n    \u003c.layout {assigns}\u003e\n      \u003ch1\u003e{@post.title}\u003c/h1\u003e\n      \u003cp\u003e\n        {raw(@post.html)}\n      \u003c/p\u003e\n    \u003c/.layout\u003e\n    \"\"\"\n  end\nend\n```\n\n2. Create your own generator file\n\n```elixir\ndefmodule MyAppWeb.Blog.Generator do\n  alias Literature.StaticPages.Generator, as: LiteratureGenerator\n\n  def generate_index_page do\n    opts =\n      [\n        publication_slug: \"blog\",\n        base_url: \"http://localhost:4000\",\n        templates: MyAppWeb.Blog.Templates,\n        write_to: :file\n      ]\n\n    LiteratureGenerator.generate(:index, opts)\n  end\nend\n```\n\nCalling `MyAppWeb.Blog.Generator.generate_index_page()` should generate an `index.html` inside your set `static_pages_storage_dir`.\nSee `Literature.StaticPages.Generator` for more information.\n\nGenerated files can be served through `Plug.Static` or through a controller.\n\n```elixir\ndefmodule MyAppWeb.BlogController do\n  use Phoenix.Controller\n\n  def index(conn, _params) do\n    static_file_path = \"/tmp/literature/static_pages/index.html\"\n\n    conn\n    |\u003e put_resp_header(\"content-type\", \"text/html; charset=utf-8\")\n    |\u003e Conn.send_file(200, static_file_path)\n  end\nend\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmartide%2Fliterature","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmartide%2Fliterature","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmartide%2Fliterature/lists"}