<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sebastian Schürmanns</title>
    <description>The latest articles on DEV Community by Sebastian Schürmanns (@trendschau).</description>
    <link>https://dev.to/trendschau</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F61074%2Fb1c5959a-ded4-4a6d-81b4-1764335c55fb.jpeg</url>
      <title>DEV Community: Sebastian Schürmanns</title>
      <link>https://dev.to/trendschau</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/trendschau"/>
    <language>en</language>
    <item>
      <title>Multilingual Websites Are Hard — AI Finally Makes Them Practical</title>
      <dc:creator>Sebastian Schürmanns</dc:creator>
      <pubDate>Mon, 05 Jan 2026 14:34:07 +0000</pubDate>
      <link>https://dev.to/trendschau/multilingual-websites-are-hard-ai-finally-makes-them-practical-4k6a</link>
      <guid>https://dev.to/trendschau/multilingual-websites-are-hard-ai-finally-makes-them-practical-4k6a</guid>
      <description>&lt;p&gt;I’ve been maintaining my open-source flat-file CMS Typemill for more than eight years. During all that time, one feature request never changed: When will Typemill support multilingual websites?&lt;/p&gt;

&lt;p&gt;The reason it took so long is simple: multilingual support is one of the hardest features to implement in a flat-file CMS. That’s also why only a handful of flat-file CMSs really support multilingual websites today, such as Kirby, Statamic, Grav, or Automad.&lt;/p&gt;

&lt;h2&gt;
  
  
  Different architectures, different trade-offs
&lt;/h2&gt;

&lt;p&gt;Flat-file CMSs solve multilingual websites in very different ways. Some systems, like Grav, store language variants directly inside the same page folder. This makes switching languages straightforward and keeps pages naturally connected.&lt;/p&gt;

&lt;p&gt;Typemill, however, follows a different approach. It builds a &lt;em&gt;full index of all pages&lt;/em&gt; to manage navigation, deeply nested content structures, and drag-and-drop ordering. This makes content structuring very flexible — but it also means that different languages cannot simply live side by side in the same folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Projects instead of folders
&lt;/h2&gt;

&lt;p&gt;The solution I ended up with was introducing &lt;strong&gt;projects&lt;/strong&gt;. Each project has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Its own content space
&lt;/li&gt;
&lt;li&gt;Its own navigation
&lt;/li&gt;
&lt;li&gt;Its own logic and index
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Projects were not originally designed for multilingual websites — they were an unplanned first step. It quickly turned out that they are useful in general. Since Typemill can also publish websites as PDF books or EPUB files, projects can also be used to manage multiple publications within a single installation.&lt;/p&gt;

&lt;p&gt;For multilingual websites, projects turned out to be a natural fit. Each language becomes its own project — isolated, clean, and independently manageable — while still being part of the same system. All that’s needed is a global index that keeps track of how language variants are linked to each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  The missing piece: AI translation
&lt;/h2&gt;

&lt;p&gt;Even with a solid structure, one major problem remained: translation workflow.&lt;/p&gt;

&lt;p&gt;Copying pages, translating articles, and updating metadata manually works for small websites. But it consumes a lot of time and quickly breaks down as a project grows.&lt;/p&gt;

&lt;p&gt;One big advantage of flat-file CMSs compared to database-driven systems is their use of &lt;a href="https://dev.to/trendschau/markdown-everywhere-why-flat-file-cms-should-be-on-the-rise-again-4366"&gt;simple Markdown files&lt;/a&gt;. Markdown is also the native output format for nearly all LLM services and APIs. This makes integrating AI services into flat-file CMSs surprisingly straightforward. Using this advantage to automate translations is, frankly, a no-brainer.&lt;/p&gt;

&lt;p&gt;Typemill already integrated AI services through a separate interface called &lt;strong&gt;Kixote&lt;/strong&gt;. To fully automate translations, you simply activate a checkbox. When you create a new language page, Typemill copies the original content into the target language project and performs one additional step: it sends the content to an AI service and stores the translated response.&lt;/p&gt;

&lt;p&gt;That’s it.&lt;/p&gt;

&lt;p&gt;Currently, auto-translation only runs when a translation page is created. If the original content changes later, translations still need to be updated manually. But that’s a comparatively small next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Multilingual support in flat-file CMSs has always been a hard problem — structurally, conceptually, and operationally. AI doesn’t solve every issue, but it removes the biggest pain point: repetitive manual work. In my view, if you’re building tools for content creators today, multilingual support should at least include an option to automate translations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watch the walkthrough
&lt;/h2&gt;

&lt;p&gt;I created a short 5-minute demo video that shows how easy it is to set up a multilingual website with Typemill using this approach:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=YDpu7_-YZ7Y" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffxt2x1mci4ivpnm68bpr.png" alt="Watch the video walkthrough" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re curious to learn more, visit the Typemill website:&lt;br&gt;&lt;br&gt;
&lt;a href="https://typemill.net" rel="noopener noreferrer"&gt;https://typemill.net&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cms</category>
      <category>multilingual</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Archiving Your ChatGPT History in Markdown</title>
      <dc:creator>Sebastian Schürmanns</dc:creator>
      <pubDate>Fri, 01 Aug 2025 11:38:44 +0000</pubDate>
      <link>https://dev.to/trendschau/archiving-your-chatgpt-history-in-markdown-3i1o</link>
      <guid>https://dev.to/trendschau/archiving-your-chatgpt-history-in-markdown-3i1o</guid>
      <description>&lt;p&gt;You probably know that you can download your whole chat history from ChatGPT, right? The export gives you a &lt;code&gt;.zip&lt;/code&gt; file containing a massive JSON file and a big HTML file.&lt;/p&gt;

&lt;p&gt;That’s fine for backup, but not great if you want to actually &lt;em&gt;use&lt;/em&gt; your data — for example, to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search across past conversations
&lt;/li&gt;
&lt;li&gt;Organize chats by topic
&lt;/li&gt;
&lt;li&gt;Build a personal knowledge base
&lt;/li&gt;
&lt;li&gt;Feed chats into a RAG pipeline
&lt;/li&gt;
&lt;li&gt;Clean up conversations for documentation
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I built a plugin for &lt;a href="https://typemill.net" rel="noopener noreferrer"&gt;Typemill&lt;/a&gt;, a small self-hosted CMS that stores content as Markdown files. The plugin lets you import your full ChatGPT archive and convert it into clean, editable Markdown pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup: Typemill + Plugin
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://typemill.net" rel="noopener noreferrer"&gt;Typemill&lt;/a&gt; is a lightweight, open-source CMS built specifically for documentation and structured content. It stores each page as a Markdown file on your server — no database needed. It’s ideal for knowledge bases, manuals, or project notes. To get started, read the &lt;a href="https://docs.typemill.net/getting-started/quickstart" rel="noopener noreferrer"&gt;quick guide in the documentation&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;You can then download the &lt;a href="https://plugins.typemill.net/chatgptarchive" rel="noopener noreferrer"&gt;ChatGPT Archive plugin&lt;/a&gt; from the plugin page, put it into the plugin folder of Typemill, and activate it in the interface. &lt;/p&gt;

&lt;h2&gt;
  
  
  What the Plugin Does
&lt;/h2&gt;

&lt;p&gt;Once you download your &lt;code&gt;.zip&lt;/code&gt; archive from OpenAI, the plugin lets you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upload the archive directly from the Typemill backend
&lt;/li&gt;
&lt;li&gt;View and select individual conversations from the archive
&lt;/li&gt;
&lt;li&gt;Choose how to sort them (chronologically or by topic)
&lt;/li&gt;
&lt;li&gt;Convert selected chats into Markdown pages
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6bnuouvxonoggy685j0i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6bnuouvxonoggy685j0i.png" alt="Upload Log" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The result: the chats are saved as editable Markdown pages in Typemill on your own server. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq51z3wsihdv2kow4lmc2.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq51z3wsihdv2kow4lmc2.webp" alt="Editor Interface of Typemill" width="800" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;You can run Typemill as a simple archive, or use the Markdown files for any other pipeline. You can also create a knowledge base based on your chats like this:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Move important chats into new folders and build your own private, structured knowledge base
&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://typemill.net/article/ai-editor-and-prompt-library" rel="noopener noreferrer"&gt;Typemill’s AI interface&lt;/a&gt; (Kixote) to summarize or extract information from chats
&lt;/li&gt;
&lt;li&gt;Create a prompt library to reuse prompts across topics
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcbimgei5orofkz6v69m1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcbimgei5orofkz6v69m1.webp" alt="AI interface of Typemill" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;This plugin is completely free and available &lt;a href="https://plugins.typemill.net/chatgptarchive" rel="noopener noreferrer"&gt;here&lt;/a&gt;. If you’ve done something similar with other tools (like Obsidian, Notion, or static site generators), I’d love to hear how you approached it.&lt;/p&gt;

&lt;p&gt;Let me know if you find it useful or run into issues!&lt;/p&gt;

</description>
      <category>chatgpt</category>
      <category>privacy</category>
      <category>markdown</category>
      <category>selfhosted</category>
    </item>
    <item>
      <title>Markdown Everywhere: Why Flat-File CMS Should Be on the Rise Again</title>
      <dc:creator>Sebastian Schürmanns</dc:creator>
      <pubDate>Fri, 14 Feb 2025 14:32:40 +0000</pubDate>
      <link>https://dev.to/trendschau/markdown-everywhere-why-flat-file-cms-should-be-on-the-rise-again-4366</link>
      <guid>https://dev.to/trendschau/markdown-everywhere-why-flat-file-cms-should-be-on-the-rise-again-4366</guid>
      <description>&lt;h2&gt;
  
  
  Markdown: The Language of the AI Age
&lt;/h2&gt;

&lt;p&gt;Markdown has quietly become the dominant format in the age of AI. Large Language Models (LLMs) process Markdown natively, making it the standard format for AI-generated content. From OpenAI’s ChatGPT to local LLMs, Markdown is the default for structured text, code snippets, and formatting.&lt;/p&gt;

&lt;p&gt;Developers have long embraced Markdown for its simplicity and portability. Now, with AI-driven workflows becoming mainstream, Markdown is moving beyond tech communities and into broader content management.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Decline of Flat-File CMS
&lt;/h2&gt;

&lt;p&gt;Flat-File CMS had their golden era around 2020. They offered lightweight, database-free content management, making them ideal for small projects. However, their popularity waned as headless CMS and SaaS-based content platforms took over.&lt;/p&gt;

&lt;p&gt;Many developers turned to API-driven solutions like Contentful or Strapi, prioritizing scalability and structured content delivery over simplicity. Meanwhile, traditional CMS like WordPress continued to dominate, even integrating REST and GraphQL APIs to stay relevant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Flat-File CMS Could Make a Comeback
&lt;/h2&gt;

&lt;p&gt;The landscape is shifting again, and several trends suggest Flat-File CMS might be due for a resurgence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AI-Generated Content Needs a Simple Format&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
AI tools generate Markdown natively, making it easier to integrate Markdown-first CMS solutions into modern content workflows.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Privacy and Data Ownership&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
With increasing concerns about cloud-based services, self-hosted solutions are regaining interest. Flat-File CMS offers full control over content without vendor lock-in.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Speed and Efficiency&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Flat-File CMS remains one of the fastest ways to serve content, especially for smaller websites.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No Dependencies, No Complexity&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Unlike headless CMS that require a backend and API layers, Flat-File CMS work out of the box with just a filesystem and Markdown files.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Decentralized Content Creation&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
More teams are embracing Git-based workflows for content collaboration. Flat-File CMS aligns well with this trend, as Markdown files can be versioned and managed in Git repositories.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Some Great Flat-File CMS Options
&lt;/h2&gt;

&lt;p&gt;Although Flat-File CMS are no longer trending like they once were, many have quietly evolved into powerful solutions. There are both feature-rich systems for advanced users and smaller niche projects for lightweight needs. Here are some of the best options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://getkirby.com/" rel="noopener noreferrer"&gt;Kirby CMS&lt;/a&gt;&lt;/strong&gt; (Commercial) – A highly flexible and lightweight system with an excellent user interface and unlimited features. Ideal for professional web developers who prefer minimal dependencies and a well-designed UI.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://statamic.com/" rel="noopener noreferrer"&gt;Statamic CMS&lt;/a&gt;&lt;/strong&gt; (Commercial) – A great choice for Laravel developers. Originally lightweight, but now a full-fledged CMS with advanced features like form builders, GraphQL, and REST APIs. If you worked with Expression Engine or Craft CMS - you will feel familiar because they have the same roots.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://getgrav.org/" rel="noopener noreferrer"&gt;Grav CMS&lt;/a&gt;&lt;/strong&gt; (Open Source) – The most well-known open-source Flat-File CMS with a large ecosystem of plugins and themes. While it may have missed some recent trends like built-in API or headless support, it still boasts the biggest community.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://typemill.net/" rel="noopener noreferrer"&gt;Typemill&lt;/a&gt;&lt;/strong&gt; (Open Source + Premium) – My own project, actively developed for 8 years. While it doesn’t focus on layout flexibility, it works perfect for documentation, knowledge bases, and other informational websites. API support is included, and AI integration is in progress.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://automad.org/" rel="noopener noreferrer"&gt;Automad&lt;/a&gt;&lt;/strong&gt; (Open Source) – A visually stunning Flat-File CMS with impressive layout options. It feels like a lightweight, self-hosted website builder – perfect for design-centric projects.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.bludit.com/" rel="noopener noreferrer"&gt;Bludit&lt;/a&gt;&lt;/strong&gt; (Open Source) – A great general-purpose Flat-File CMS, ideal for simple websites like portfolios. While development has slowed recently, it still offers everything needed for small-scale projects.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.htmly.com/" rel="noopener noreferrer"&gt;HTMLy&lt;/a&gt;&lt;/strong&gt; (Open Source) – My favorite pick for small, ultra-simple blogs. It’s not a “5-minute setup” CMS – it’s a “5-second setup” CMS.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/datenstrom/yellow" rel="noopener noreferrer"&gt;Yellow&lt;/a&gt;&lt;/strong&gt; (Open Source) – A unique and quirky project that has been in active development for years. If you love niche, independent projects, check it out!
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are more Flat-File CMS out there, but these are my favorites. If you're curious, just explore – you'll find plenty of great options!&lt;/p&gt;

</description>
      <category>markdown</category>
      <category>cms</category>
      <category>ai</category>
      <category>flatfile</category>
    </item>
    <item>
      <title>Open Source Needs a New Business Model – What if AI Could Help?</title>
      <dc:creator>Sebastian Schürmanns</dc:creator>
      <pubDate>Wed, 12 Feb 2025 18:35:06 +0000</pubDate>
      <link>https://dev.to/trendschau/open-source-needs-a-new-business-model-what-if-ai-could-help-4p9o</link>
      <guid>https://dev.to/trendschau/open-source-needs-a-new-business-model-what-if-ai-could-help-4p9o</guid>
      <description>&lt;p&gt;Maintaining an open-source project is tough. Finding a sustainable way to fund it? Even tougher.&lt;/p&gt;

&lt;p&gt;I’ve been running &lt;a href="https://typemill.net" rel="noopener noreferrer"&gt;Typemill&lt;/a&gt;, a small flat-file CMS for publishing documentation and handbooks, for eight years now. Last year, I introduced paid plugins as a way to support development. It hasn't worked well. Let’s be honest – competing with massive plugin ecosystems is nearly impossible for smaller projects.&lt;/p&gt;

&lt;p&gt;But maybe a massive plugin ecosystem &lt;strong&gt;won’t even matter in the future&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What if we rethink the model?
&lt;/h2&gt;

&lt;p&gt;When ChatGPT first launched, I immediately tried using it to generate Typemill plugins. It failed badly, probably because Typemill is too small and niche for the AI to understand.&lt;/p&gt;

&lt;p&gt;But things are evolving.&lt;/p&gt;

&lt;p&gt;AI-assisted software generation is already making waves – just look at &lt;strong&gt;Lovable&lt;/strong&gt; and similar tools. These services are already shifting the paradigm from "buy" to "make" in many companies. Open-source software could serve as a perfect foundation for such custom solutions. &lt;/p&gt;

&lt;p&gt;What if, instead of selling pre-built plugins, &lt;strong&gt;open-source projects offered AI-powered customization services&lt;/strong&gt;?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users get &lt;strong&gt;exactly what they need&lt;/strong&gt;, tailored to their setup – without waiting for developers to create and sell the perfect plugin.&lt;/li&gt;
&lt;li&gt;Open-source maintainers get &lt;strong&gt;a sustainable revenue stream&lt;/strong&gt; beyond traditional donations or sponsorships.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  If I Could Build the Perfect Service...
&lt;/h2&gt;

&lt;p&gt;Imagine an &lt;strong&gt;AI-powered open-source development service&lt;/strong&gt; that works like a software reseller (e.g., Paddle):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maintainers set up an &lt;strong&gt;AI coding assistant&lt;/strong&gt;, trained specifically for their project.&lt;/li&gt;
&lt;li&gt;Users pay to &lt;strong&gt;access AI-generated customizations&lt;/strong&gt;, making it easier to adapt the software to their needs.&lt;/li&gt;
&lt;li&gt;The open-source project &lt;strong&gt;earns revenue&lt;/strong&gt; while staying lean and flexible.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Could this work?
&lt;/h2&gt;

&lt;p&gt;Honestly, I don’t know, and we are probably not at this point yet. But it feels like a model worth exploring. AI-assisted coding is improving fast, and this approach could make open-source projects &lt;strong&gt;more adaptable, financially viable, and easier to maintain&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What do you think? Have you seen services like this? Let’s discuss!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>lovable</category>
      <category>opensource</category>
      <category>monetization</category>
    </item>
    <item>
      <title>Crosspost to DEV community with Typemill</title>
      <dc:creator>Sebastian Schürmanns</dc:creator>
      <pubDate>Thu, 07 May 2020 07:29:04 +0000</pubDate>
      <link>https://dev.to/trendschau/crosspost-to-dev-community-with-typemill-1nk8</link>
      <guid>https://dev.to/trendschau/crosspost-to-dev-community-with-typemill-1nk8</guid>
      <description>&lt;p&gt;This article was originally published on &lt;a href="https://trendschau.net/blog/crosspost-dev-community-typemill" rel="noopener noreferrer"&gt;trendschau.net&lt;/a&gt;, but you will probably read it on &lt;a href="https://dev.to"&gt;dev.to&lt;/a&gt; first. How come?&lt;/p&gt;

&lt;p&gt;Some weeks ago a dev-user asked other members how they &lt;a href="https://dev.to/rimutaka/dev-to-writers-what-md-tools-do-you-use-1pad"&gt;crosspost their markdown articles&lt;/a&gt; to the dev-platform. Since I develop a small flat-file cms called &lt;a href="https://typemill.net" rel="noopener noreferrer"&gt;Typemill&lt;/a&gt; that works with markdown, I asked myself if it is worth to create a little plugin for such crossposts. I checked the &lt;a href="https://docs.dev.to/api/"&gt;dev-API&lt;/a&gt;, wrote about 200 lines of code and voila: The first crosspost is online.&lt;/p&gt;

&lt;p&gt;This is a screenshot of the Typemill admin-ui for this article:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F413efhrel8n41ezlkwyc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F413efhrel8n41ezlkwyc.png" alt="Screenshot of crosspost-tab in Typemill" width="800" height="925"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The plugin adds a new tab called "dev.to" to each article and in that tab you can manage the crosspost. You can activate the crosspost, you can choose between draft or publish and you can add tags or series. The plugin will synchronise the articles (and rewrite the image-urls) whenever you publish or update the article on Typemill. It will also store and display the response from dev.to and display a little preview.&lt;/p&gt;

&lt;p&gt;It is pretty simple to create a plugin like this with Typemill:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All you need is a YAML-file, a small vue-component and a php-file with some logic.&lt;/li&gt;
&lt;li&gt;With the YAML-file you can define the forms and fields for your tab.&lt;/li&gt;
&lt;li&gt;With the vue-component you can render the tab and also add the vue-logic that you want.&lt;/li&gt;
&lt;li&gt;With the php-file you can listen to the typemill-events (I wrote about the &lt;a href="https://dev.to/trendschau/how-i-created-my-first-plugin-system--400"&gt;event-dispatcher here&lt;/a&gt;) and make the api calls.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Typemill is open source. Don't expect a high level system or a super robust and bug-free software: It is still a side project with a small community and we work hard to improve it with each release. If you have a close look to the &lt;a href="https://github.com/typemill/typemill" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;: 8 more stars and we have 100 !!!&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>webdev</category>
      <category>markdown</category>
      <category>api</category>
    </item>
    <item>
      <title>Explain RBAC vs ACL Like I'm Five</title>
      <dc:creator>Sebastian Schürmanns</dc:creator>
      <pubDate>Thu, 12 Mar 2020 07:02:25 +0000</pubDate>
      <link>https://dev.to/trendschau/explain-rbac-vs-acl-like-i-m-five-4gpa</link>
      <guid>https://dev.to/trendschau/explain-rbac-vs-acl-like-i-m-five-4gpa</guid>
      <description>&lt;p&gt;HeyHo DEVs, &lt;/p&gt;

&lt;p&gt;I want to implement a permission system for my small flat file cms &lt;a href="https://typemill.net" rel="noopener noreferrer"&gt;Typemill&lt;/a&gt; and I am not super sure, if I should follow RBAC or ACL. What the heck is the difference? I want to create roles like this: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"reader" (public access), &lt;/li&gt;
&lt;li&gt;"member" with auth and access to "member" content&lt;/li&gt;
&lt;li&gt;"customer" with auth and access to "paid" content&lt;/li&gt;
&lt;li&gt;"author" with auth and access to his own articles&lt;/li&gt;
&lt;li&gt;"editor" with auth and access to all articles, but no rights like publishing&lt;/li&gt;
&lt;li&gt;"publisher" with publishing rights.&lt;/li&gt;
&lt;li&gt;"admin" with access to admin settings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything with enough flexibility.&lt;/p&gt;

&lt;p&gt;So what should I use? &lt;/p&gt;

</description>
      <category>explainlikeimfive</category>
    </item>
    <item>
      <title>When and how did you learn to code?</title>
      <dc:creator>Sebastian Schürmanns</dc:creator>
      <pubDate>Wed, 18 Sep 2019 14:48:03 +0000</pubDate>
      <link>https://dev.to/trendschau/when-and-how-did-you-learn-to-code-3af6</link>
      <guid>https://dev.to/trendschau/when-and-how-did-you-learn-to-code-3af6</guid>
      <description>&lt;p&gt;I think coding carreers are still pretty diverse and that is what I love so much about it. I was always very bad in math and had not the self-confidence to start with coding, so I was some kind of late starter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Starting: At the age of 35&lt;/li&gt;
&lt;li&gt;Language: PHP&lt;/li&gt;
&lt;li&gt;First steps with: The book "php for kids" and a video-tutorial on how to create a tumblr-like blog-platform :D&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>beginners</category>
      <category>learn</category>
      <category>code</category>
    </item>
    <item>
      <title>Explain How I Secure my First Web-Application Like I Am Five</title>
      <dc:creator>Sebastian Schürmanns</dc:creator>
      <pubDate>Fri, 05 Jul 2019 08:47:05 +0000</pubDate>
      <link>https://dev.to/trendschau/explain-how-i-secure-my-first-web-application-like-i-am-five-p3h</link>
      <guid>https://dev.to/trendschau/explain-how-i-secure-my-first-web-application-like-i-am-five-p3h</guid>
      <description>&lt;p&gt;Yes, I am your super nerdy child and proudly present you my first self-coded web application. Of course I want to publish it live and show it to all my kindergarden-friends. You are super proud, but you also want to prevent me from a security-nightmare with my first running web-application. Your child wouldn't be such a lovely nerd if it had not already duckduckgone some security-related terms. Now it is your turn to explain what it means, what it prevents from and how it works (super basically):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTPS&lt;/li&gt;
&lt;li&gt;Input Validation&lt;/li&gt;
&lt;li&gt;Authentication (oauth, JWT, more?)&lt;/li&gt;
&lt;li&gt;Autorization&lt;/li&gt;
&lt;li&gt;Security Headers&lt;/li&gt;
&lt;li&gt;CSRF&lt;/li&gt;
&lt;li&gt;CORS&lt;/li&gt;
&lt;li&gt;CSP&lt;/li&gt;
&lt;li&gt;Captcha&lt;/li&gt;
&lt;li&gt;Honeypot&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;... and maybe you are missing some very basic random stuff that is important to know before I go live?&lt;/p&gt;

</description>
      <category>security</category>
      <category>explainlikeimfive</category>
      <category>help</category>
    </item>
    <item>
      <title>Creating a Block Editor with Markdown and Vue.js</title>
      <dc:creator>Sebastian Schürmanns</dc:creator>
      <pubDate>Fri, 07 Dec 2018 10:25:17 +0000</pubDate>
      <link>https://dev.to/trendschau/creating-a-block-editor-with-markdown-and-vuejs-o14</link>
      <guid>https://dev.to/trendschau/creating-a-block-editor-with-markdown-and-vuejs-o14</guid>
      <description>&lt;p&gt;Editing blocks of content is highly popular these days. WordPress is one of the last kids on the block, other content management systems like AEM or Magnolia follow the concept of content components since more than a decade now. So while I coded my small flat file cms called &lt;a href="https://typemill.net" rel="noopener noreferrer"&gt;Typmill.net&lt;/a&gt; as a side project, I wondered if some kind of block editing could also bridge the gap between markdown and the not so tech-savvy mainstream-editors.&lt;/p&gt;

&lt;p&gt;If you want to build a new editor-experience, then you can use one of the new editor-frameworks like prosemirror or slate, which also provide a realtime-wysiwyg-mode for markdown. However, I am not a professional software-developer and these frameworks are pretty complicated to start with. But I started to use Vue.js some month ago and so I decided to code my own block-editing-experience with Vue.sj from scratch. Don't expect a super high level frontend editor here, it is more like an experimental hacking and it is tightly coupled to Typemill, but the result might still be interesting:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftypemill.net%2Fmedia%2Fdemo-2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftypemill.net%2Fmedia%2Fdemo-2.gif" alt="Block editing with markdown" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The typemill block editor as of november 2018&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;It is a quite big project, so I cannot show much code here, but maybe some basic ideas of the block-editing with markdown.&lt;/p&gt;
&lt;h2&gt;
  
  
  Markdown are Blocks by Definition
&lt;/h2&gt;

&lt;p&gt;I am not sure if you are aware that markdown works with blocks by definition, because markdown uses two linebreaks to separate each block (paragraph). So you can transform a valid and normalized markdown-file into a simple array of content blocks with just one line of PHP-code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  $markdownArray = explode("\n\n", $markdown);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You have to deal with some edge-cases like code-blocks, but basically it is as simple as that.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Backend Approach
&lt;/h2&gt;

&lt;p&gt;With Vue.js you can fetch the markdown-array with an API in a JSON-format and then create your website in the frontend. I decided against that standard-way from two reasons: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I already use a highly interactive navigation built with vue, so the page renders very slow if you add the content-part on top of that.&lt;/li&gt;
&lt;li&gt;If I transform markdown to html in the frontend, then I have to maintain and extend two libraries: one markdown-parser in the backend (I use parsedown with several extensions) and one in the frontend (vue markdown parser for example).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I decided for an uncommon way and used the backend for most of the tasks. With a php-library like parsedown it is pretty easy to transform the markdown-blocks into html-blocks similar like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$parsedown  = new Parsedown();
$htmlArray = [];

foreach($markdownArray as $markdownBlock)
{
    $htmlArray[] = $parsedown-&amp;gt;text($markdownBlock);    
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the frontend, I can print out each html-block with a simple loop. Rendering the page in the backend makes the initial page load pretty fast. At the same time, Vue.js enters the stage because I can print out each block into a vue component like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php foreach($htmlArray as $key =&amp;gt; $htmlBlock): ?&amp;gt;

   &amp;lt;content-block&amp;gt;
    &amp;lt;div class="blox" @click.prevent="setData( $event )" data-id="&amp;lt;?php echo $key; ?&amp;gt;"&amp;gt;&amp;lt;?php echo $htmlBlock; ?&amp;gt;&amp;lt;/div&amp;gt;
   &amp;lt;/content-block&amp;gt;

&amp;lt;?php endforeach; ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Vue-Part
&lt;/h2&gt;

&lt;p&gt;The vue-part is pretty complex (and chaotic, sorry for that), so I cannot show code-examples but only explain the basic ideas. So basically I use three parts: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A vue app with some general tasks.&lt;/li&gt;
&lt;li&gt;A content-component as a frame or wrapper for each content-block.&lt;/li&gt;
&lt;li&gt;Dynamic components for each content-type (like paragraph, images and more).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;strong&gt;vue app&lt;/strong&gt; loads the markdown-array in a JSON-format with an API-call after the page has loaded. I can connect the html on the page and the markdown in the vue-data with the ID now. I do this with the setData-method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@click.prevent="setData( $event )"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the data-id I can get the corresponding markdown part and use it in my content-component. You might think that this is a bit hacky, but I explained the reasons before...&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;content-component&lt;/strong&gt; is the frame that does all the general stuff: It gets the markdown-data from the vue-app for each block, it switches between the html-preview-mode and the markdown-edit-mode, and each content component has a button to save, cancel and delete a block. When I save the block, then I send the markdown-data to the backend. In the backend I store the block in the file (work with the ID again to get the appropriate block), transform the markdown block into html again and send it in the response back to the frontend. &lt;/p&gt;

&lt;p&gt;The most exciting part are the &lt;strong&gt;dynamic component&lt;/strong&gt;. This is the concept of vue, where you can switch a component dynamically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- Component changes when currentTabComponent changes --&amp;gt;
&amp;lt;component v-bind:is="currentTabComponent"&amp;gt;&amp;lt;/component&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vue explains the concept &lt;a href="https://vuejs.org/v2/guide/components.html#Dynamic-Components" rel="noopener noreferrer"&gt;here&lt;/a&gt; and also provides some &lt;a href="https://jsfiddle.net/chrisvfritz/o3nycadu/" rel="noopener noreferrer"&gt;fiddles&lt;/a&gt; to play with. &lt;/p&gt;

&lt;p&gt;I use this concept to open different components for different content types. The basic content type is a text-paragraph and this opens in the standard textarea-component. But I just added another component for handling images and a lot more will follow. Handling images can become pretty painful in some content-management-systems, so I think I found a quite user-friendly way:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftypemill.net%2Fmedia%2Flive%2F5c0a48b44a765-live.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftypemill.net%2Fmedia%2Flive%2F5c0a48b44a765-live.gif" alt="Image Component of Typemill" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to have a look (on &lt;a href="https://typemill.net" rel="noopener noreferrer"&gt;typemill&lt;/a&gt; or &lt;a href="https://github.com/trendschau/typemill" rel="noopener noreferrer"&gt;github&lt;/a&gt;), then don't expect high level code, as I said I am a hobby-developer and not a professional. &lt;/p&gt;

</description>
      <category>cms</category>
      <category>vue</category>
      <category>markdown</category>
    </item>
    <item>
      <title>Which Markdown Editor Do You Recommend For My Application?</title>
      <dc:creator>Sebastian Schürmanns</dc:creator>
      <pubDate>Fri, 08 Jun 2018 07:01:51 +0000</pubDate>
      <link>https://dev.to/trendschau/which-markdown-editor-do-you-recommend-for-my-application-297j</link>
      <guid>https://dev.to/trendschau/which-markdown-editor-do-you-recommend-for-my-application-297j</guid>
      <description>&lt;p&gt;Hey all, right now I am building a minimal flat file cms and I am looking for a nice markdown editor. There are two main requirements: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The cms is for non-techies, so the markdown-editor should have the option to bridge the gap with some kind of optional wysiwyg mode.&lt;/li&gt;
&lt;li&gt;On server side I use extensions that support tags like [TOC] and integration of KaTeX, MathJax and other special stuff, so the markdown syntax of the editor should be extensible in some way.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Right now I thing about: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://slatejs.org/#/rich-text" rel="noopener noreferrer"&gt;Slatejs&lt;/a&gt;: My favourite so far, similar to prosemirror but easier to start with (based on react).&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://prosemirror.net/" rel="noopener noreferrer"&gt;Prosemirror&lt;/a&gt;: Looks good but learning curve is pretty high I think. &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://simplemde.com/" rel="noopener noreferrer"&gt;SimpleMDE&lt;/a&gt;: Pretty fast, more traditional and based on codemirror, but I don't think that it is easy to extend?&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://ui.toast.com/tui-editor/" rel="noopener noreferrer"&gt;Tui&lt;/a&gt;: Looks good but also a bit obscure. Based on codemirror, too. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which one would you recommend for my requirements?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>question</category>
      <category>markdown</category>
    </item>
    <item>
      <title>How to engage people and find contributors for an open source project on github?</title>
      <dc:creator>Sebastian Schürmanns</dc:creator>
      <pubDate>Mon, 02 Apr 2018 07:30:55 +0000</pubDate>
      <link>https://dev.to/trendschau/how-to-engage-people-and-find-contributors-for-an-open-source-project-on-github-3n86</link>
      <guid>https://dev.to/trendschau/how-to-engage-people-and-find-contributors-for-an-open-source-project-on-github-3n86</guid>
      <description>&lt;p&gt;Yes, we all know that this is very hard, but maybe you have some tricks or strategies, or you have engaged in an open source project yourself and still remember, why?&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>How I Created my First Plugin-System</title>
      <dc:creator>Sebastian Schürmanns</dc:creator>
      <pubDate>Fri, 16 Mar 2018 18:14:43 +0000</pubDate>
      <link>https://dev.to/trendschau/how-i-created-my-first-plugin-system--400</link>
      <guid>https://dev.to/trendschau/how-i-created-my-first-plugin-system--400</guid>
      <description>&lt;p&gt;Hi all, &lt;/p&gt;

&lt;p&gt;I am pretty sure that most of you have already written a plugin for a CMS or any other software. But did you ever think about creating your own plugin system for your own software project, so that others can plug into your software and enhance it without touching the core code? It is a bit more tricky than crafting a simple plugin. But it is way easier than you might think. And for me, it changed the way I code new projects forever.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Coded Projects Before
&lt;/h2&gt;

&lt;p&gt;I am NOT a professional developer, but a product owner and historian (originally). I code projects just for fun, for distraction and for therapeutic reasons. I started with PHP at the age of 35 and I learned it with the book "PHP for Kids". Growing up mentally, I learned how to write a tumblr-like application with dirty old spaghetti code. It was already organized in a MVC way, but I still ran into the usual mess of chaotic and unmaintainable code. To solve these problems, I switched to object orientation and namespacing, I started to use the PHP-microframework Slim and I familiarized myself with GitHub and Composer. Everything was organized in models, controllers, classes, methods, configuration files and external libraries, so there was a gleam of order in all that chaos. But this still does not help in certain situations ...&lt;/p&gt;

&lt;h2&gt;
  
  
  Asking the Right Questions
&lt;/h2&gt;

&lt;p&gt;This situation popped up with my little side-project called &lt;a href="https://github.com/trendschau/typemill" rel="noopener noreferrer"&gt;Typemill&lt;/a&gt;, that should become a simple flat-file-cms for writers once it is grown up. For a real CMS I had to find a way to let other coders enhance my system with plugins, addons, extensions or whatever you want to call it. So I asked some search engines ...&lt;/p&gt;

&lt;p&gt;... and I was pretty astonished, that all these search engines don't know much about "plugin systems" at all. Poor answers are usually a result of wrong questions. So I digged deeper and found out, that plugin systems follow special programming patterns (and to be honest, I didn't care much about programming patterns before). There are several patterns for plugin systems (read Anthony Ferraras &lt;a href="https://blog.ircmaxell.com/2012/03/handling-plugins-in-php.html" rel="noopener noreferrer"&gt;article about patterns for plugins&lt;/a&gt;), but the most common pattern is the "mediator pattern". And the mediator pattern is also used for something called "event driven programming" and "event dispatching".&lt;/p&gt;

&lt;p&gt;I am familiar with "events" in JavaScript, but I didn't know, that you can use "events" in server side code, too. In fact, not only content management systems use events for their plugin systems, but most of the frameworks like Zend, Laravel and others provide a build-in event system out of the box. &lt;/p&gt;

&lt;h2&gt;
  
  
  How I Tried to Understand Event Systems
&lt;/h2&gt;

&lt;p&gt;I tried to wrap my head around "event systems" by comparing them with IFTTT: If this happens, than do that. In fact, you can describe all plugins with a simple and human readable IFTTT-logic like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the application has generated a html-page, then add a newsletter subscription form to the page (oh nooo, don't do that, we all hate them!!!!)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can even go a step further and describe the logic with a simple array like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;['onHtmlLoaded' =&amp;gt; 'addSubscriptionFormMethod']

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key of the array (left side) is the name of the event and the value of the array (right side) is the name of your method. So if the event on the left side happens, then the methods of the right side should be called. If this than that. Pretty simple.&lt;/p&gt;

&lt;p&gt;To get this simple logic to work, you need to create a special script often called "Event Dispatcher". But before you start coding, think about using one of the excellent libraries out there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working With The EventDispatcher
&lt;/h2&gt;

&lt;p&gt;An event dispatcher is not too complicated and there are some very &lt;a href="https://github.com/mapkyca/simple-event-dispatcher" rel="noopener noreferrer"&gt;lightweight implementations&lt;/a&gt; with about 100 lines of code. What they do is always the same: They create and manage an array of event-names and event-listeners like shown above. The event-listeners are callables (method-names). So if you know the event-name, than it is no problem to call the subscribed methods with a handy php-function like &lt;code&gt;call_user_func()&lt;/code&gt; or &lt;code&gt;call_user_func_array()&lt;/code&gt;. And that is exactly what all these event dispatchers do. &lt;/p&gt;

&lt;p&gt;The most widespread EventDispatcher-library is the &lt;a href="http://symfony.com/doc/current/components/event_dispatcher.html" rel="noopener noreferrer"&gt;Symfony Event Dispatcher&lt;/a&gt;. It provides something like this: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It manages the described array of event-names and callables.&lt;/li&gt;
&lt;li&gt;It allows you to add new "listeners" or "subscribers" to this array. The "listeners" or "subsribers" are your plugins.&lt;/li&gt;
&lt;li&gt;It allows you to "fire" (or "dispatch") events in your application, so that all the subscribers (plugin-method) for this event get called.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can check the &lt;a href="https://github.com/symfony/event-dispatcher/blob/master/EventDispatcher.php" rel="noopener noreferrer"&gt;dispatcher-class&lt;/a&gt; of Symfony (just 240 lines) and you will be surprised how simple everything is. &lt;/p&gt;

&lt;h2&gt;
  
  
  Initiate the Dispatcher
&lt;/h2&gt;

&lt;p&gt;We will create our little newsletter-plugin now to demonstrate the dispatcher with a real life example. Let's install the Symfony EventDispatcher with GitHub and Composer and then initiate it somewhere at the beginning of our code (for example in the beginning of an index.php file):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use Symfony\Component\EventDispatcher\EventDispatcher;

$dispatcher = new EventDispatcher();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can work with the EventDispatcher. We will do it in the following steps: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We will define a new event called "onHtmlLoaded".&lt;/li&gt;
&lt;li&gt;We will fire the event in your application.&lt;/li&gt;
&lt;li&gt;We will write a newsletter-plugin that subscribes to this event.&lt;/li&gt;
&lt;li&gt;We will get, manipulate and return data with the plugin.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Define An Event
&lt;/h2&gt;

&lt;p&gt;Before you can use your own events in the Symfony EventDispatcher you have to define them. A new event can be defined with an empty class like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace Typemill\Events;

use Symfony\Component\EventDispatcher\Event;

class onHtmlLoaded extends Event
{

}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All new events have to extend the base "Event" of Symfony. Later, we will define some helpful methods in this class, but for now an empty class is enough. &lt;/p&gt;

&lt;p&gt;To keep your code organized, it is a good idea to create a new folder called "events" to collect all event definitions in a central place. &lt;/p&gt;

&lt;h2&gt;
  
  
  Fire The Event
&lt;/h2&gt;

&lt;p&gt;Let's go back to our application and fire the new event. This can be done with a single line of code. &lt;/p&gt;

&lt;p&gt;For our newsletter plugin, we want to fire the event right after the HTML-content is loaded. In Typemill, I use the parsedown-library to transform the markdown-text into html. So I add this simple line of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$contentHTML = $parsedown-&amp;gt;text($contentMarkdown);

$dispatcher-&amp;gt;dispatch('onHtmlLoaded', new Typemill\Events\onHtmlLoaded());

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dispatch-method accepts two arguments: The name of the event and the event object. That is all. Now the event dispatcher uses the internal array and calls all plugin-methods, that have subscribed to this event. &lt;/p&gt;

&lt;p&gt;But wait, there are no plugins yet. So let us create our newsletter-plugin now. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Newsletter-Plugin: Subscribe to Events
&lt;/h2&gt;

&lt;p&gt;I created a new "plugin"-folder for all plugins and in there I created a folder called "newsletter". The class for our newsletter-plugin looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace Plugins\Newsletter;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Typemill\Events\onHtmlReady;

class Newsletter
{
    public static function getSubscribedEvents()
    {
        return array('onHtmlLoaded' =&amp;gt; 'myNewsletterMethod');
    }

    public function myNewsletterMethod()
    {
        die('I will add the newsletter to the html-page one day');
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally there is the array, that we have mentioned at the beginning of this article. And this array looks exactly the same: The name of the event is on the left side and the name of the plugin-method is on the right side. If this than that. &lt;/p&gt;

&lt;p&gt;The method, that returns the array, is always &lt;code&gt;getSubscribedEvents()&lt;/code&gt; and it has to be a public static method. The Symfony EventDispatcher will simply check all classes for the existence of this method and add the array to its central array of events and listeners or subscribers. It is as simple as that. &lt;/p&gt;

&lt;h2&gt;
  
  
  Load Your Plugins
&lt;/h2&gt;

&lt;p&gt;The last step is still missing: We have to initialize our plugin-class and add it tho the dispatcher, otherwise the dispatcher does not know about its existence. We should load all plugins right at the beginning of our application and add a plugin to the dispatcher like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$dispatcher = new EventDispatcher();

....
....

$dispatcher-&amp;gt;addSubscriber(new \Plugins\Newsletter()); 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course you don't want to add all plugins manually. For Typemill, I wrote a separate class, that scans all the plugin-folders, performs some checks and returns the fully qualified classnames, so that you can do something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$dispatcher = new EventDispatcher();

...

$plugins = new Typemill\Plugins();
$pluginClassnames = $plugins-&amp;gt;load();

foreach($pluginClassnames as $pluginClassname)
{
  $dispatcher-&amp;gt;addSubscriber(new $pluginClassname());
} 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, I left out a lot of details, but this should be enough to get started. &lt;/p&gt;

&lt;p&gt;Let us check what we have so far: If we visit a page in Typemill now, then the event "onHtmlReady" gets fired and the subscribed newsletter plugin get's called. Right now, the newsletter method simply kills the application and prints out the words "I will add the newsletter to the html-page one day".&lt;/p&gt;

&lt;p&gt;It works. But it does not really feel like a cool plugin. So lets make it a bit more useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manipulating Data With The Plugin
&lt;/h2&gt;

&lt;p&gt;To create our newsletter-plugin, we want to get the html-content, add the newsletter subscription form and return everything to the application again. &lt;/p&gt;

&lt;p&gt;To do this, we can simply add some getters and setters to the event class, that we have defined before. Let's do it this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace Typemill\Events;

use Symfony\Component\EventDispatcher\Event;

class onHtmlLoaded extends Event
{

    protected $data;

    public function __construct($data)
    {
        $this-&amp;gt;data = $data;
    }

    public function getData()
    {
        return $this-&amp;gt;data;
    }

    public function setData($data)
    {
        $this-&amp;gt;data = $data;
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we fire an event in our application, we can simply pass some data like this now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$contentHTML = $parsedown-&amp;gt;text($contentMarkdown);

$dispatcher-&amp;gt;dispatch('onHtmlLoaded', new Typemill\Events\onHtmlLoaded($contentHTML));

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally we can access and return the data in our newsletter plugin like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Newsletter
{

    public static function getSubscribedEvents()

    {
        return array('onHtmlLoaded' =&amp;gt; 'myNewsletterMethod');

    }

    public function myNewsletterMethod($data)
    {
        $content = $data-&amp;gt;getData(); 

        $content .= '&amp;lt;p&amp;gt;I am the newsletter subscription form&amp;lt;/p&amp;gt;';

        $data-&amp;gt;setData($content);
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have added our newsletter form to the content and returned everything to the application. The final trick is, that our application uses the manipulated data from now on. And this can be done as simple as that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$contentHTML = $parsedown-&amp;gt;text($contentMarkdown);

$contentHTML = $dispatcher-&amp;gt;dispatch('onHtmlLoaded', new Typemill\Events\onhtmlLoaded($contentHTML))-&amp;gt;getData();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Voila, the application will now send everything to the Twig template engine and display the page to the user. And we have added a newsletter-form to this page without touching any core file of the application. Our first plugin is done!&lt;/p&gt;

&lt;h2&gt;
  
  
  Events and Your Application's Life Cycle
&lt;/h2&gt;

&lt;p&gt;In real life you want to provide a lot of entry points to your application, so that client coders can manipulate different data in a lot of steps. So your event-system will usually follow the life cycle of your application and fire events in each step of the life cicle. For Typemill, it looks similar to this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The application loads all settings.&lt;/li&gt;
&lt;li&gt;The application loads all plugins.&lt;/li&gt;
&lt;li&gt;The application loads a template engine like Twig.&lt;/li&gt;
&lt;li&gt;The application routes to a controller, that creates the page.&lt;/li&gt;
&lt;li&gt;The controller loads the requested content file.&lt;/li&gt;
&lt;li&gt;The controller transforms the markdown content into HTML.&lt;/li&gt;
&lt;li&gt;The controller collects some other data (e.g. for a navigation).&lt;/li&gt;
&lt;li&gt;The controller sends all the data to the template engine.&lt;/li&gt;
&lt;li&gt;The template engine displays the page to the user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The smaller the steps, the greater the flexibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  You Just Created Your Own Plugin System
&lt;/h2&gt;

&lt;p&gt;The code above is pretty rough and not much more than you can already read in the documentation of the symfony event dispatcher itself. And if you want to create a really useful plugin system, you have to add a lot of details: How should all these plugins get loaded? How can users configure your plugins? Which useful methods should you add to a basic plugin class? How can you add new routes to your application with a plugin?&lt;/p&gt;

&lt;p&gt;A lot of open questions, but the basics are done. And for me it was really helpful to find all these ressources and examples. If you want to create your own plugin systems, you can check some real life implementations out there (I used Grav, Leafpub and many others to find my own way to integrate a plugin system into Typemill).&lt;/p&gt;

&lt;p&gt;But even if you don't want to create your own plugin system, then this kind of event driven programming is still a great way to decouple your code and to bring a bit more order into the chaos of a new code-project.&lt;/p&gt;

</description>
      <category>php</category>
      <category>eventdispatcher</category>
      <category>plugins</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
