When I started to use and to explore Cockpit CMS (v1) in 2018, I really loved it for its simplicity, modularity and performance. A few things might have been too simple, but there always was an easy way to change or add behavior.
Back then I didn’t care enough about accessibility. If I had, I probably wouldn’t have started using it.
Cockpit CMS v2 is less configurable (or hackable) than v1, there is no addon eco system (yet), a new community needs to grow and it has the same (and some new) accessibility issues as v1.
My main concerns about v2 are:
- very bad accessibility
- handling of (partial) translated data
- hard coded
_state
- spaces
- code and folder structure
In the next sections I’ll go into more details. Some issues are interconnected.
Problems
spaces
- fragile architecture with Multiton pattern
- no real separation from parent addons/modules
- urls must start with
:
- looks ugly, probably only useful with a reverse proxy - V1 worked with zero variables and only one function (
cockpit()
) in the global scope. Now$GLOBALS['APP']
is necessary for async calls, which would otherwise break the custom error handler when using spaces. The global variable is also necessary for three helper functions in the System module, which might break otherwise when using spaces.
To be honest - I don’t understand, why I would want to use spaces. I can change storage paths and custom addons folders via config.php
on my own conditions (e. g. multiple domains, first url segment, port).
error handling
- custom error handler eats instant feedback while developing - I always have to press F5 in an sqlite viewer application to read the last error
- some errors are too big to be written into database, e. g. because of
Lime\App
as constructor argument, so they don’t even show up in the custom error log table (they do, but as a null value) - it doesn’t make sense to run the custom error handler for cli usage
- also the constant
APP_ADMIN
occurs only once in the whole source code: to activate the custom error handler
state handling
V1 had no state handling, which was fine. I normally used a boolean field published
for custom state handling. Others used a text/select field to handle “published” and “draft” states.
Now three states are implemented:
-1
: archived0
: not published1
: published
Adding more or other custom states is not really possible.
I’m thinking about using the state functionality for custom states like the ones from Publ:
# source (2023-07-05): https://github.com/PlaidWeb/Publ/blob/main/publ/model.py#L28
""" The status of the entry """
DRAFT = 0 # Entry should not be rendered
HIDDEN = 1 # Entry should be shown via direct link, but not shown on a view
UNLISTED = 1 # Synonym for HIDDEN
PUBLISHED = 2 # Entry is visible
SCHEDULED = 3 # Entry will be visible in the future
GONE = 4 # Entry is gone, won't be coming back
DELETED = 4 # synonym for GONE
ILLEGAL = 5 # taken down for legal reasons (error code 451)
BURNT = 5
BURNED = 5
DMCA = 5
TEAPOT = 6 # RFC 2324
A workaround might be to set the _state
of all items to published by default, to add a custom field and use the event system to handle custom states. Because states are a core feature, they also must be disabled/hidden in multiple places in the admin ui now if I want to bypass it completely. This sounds fragile, because future updates might break these changes.
localized content
It feels like localisation is just a nice-to-have on top of single language data. Especially partial translated data can’t be handled well.
required
attribute only cares about the default language- core
_state
handling is combined for all languages - publishing the default language now and translating later is not possible - Using the core rest api endpoints applies language processing by default. This is problematic for multiple reasons:
- If some fields aren’t translated, the default language is used as a fallback. The information, that the language is different is missing, so I can’t write proper html markup with the right lang attribute for these data points.
- In v1 I was able to get all translations with an api call and doing comparisons about partial translated data later. Now I have to write a custom api endpoint to get all translations at once.
localized web ui
The i18n handling should have namespaces/annotations to care about similar written words with different meanings (verb/noun, per module). This was also a problem in v1.
accessibility
In short: It sucks.
It feels like a nice-to-have, that might be implemented later. Especially with the EU Web Accessibility Action Plan (German: Barrierefreiheitsstärkungsgesetz), this topic should get a higher priority very soon.
Some examples:
- fixed/sticky elements prevent keyboard navigation (form fields are hidden behind action bar or header)
- many ui elements aren’t accessible via keyboard at all
- font size too small
- bad contrast (at least in dark mode with white text on blue)
- I can’t remember the meaning of the icons in left side bar. So I always have to hover with my mouse over the icon to see a tooltip. Expanding the menu to actually read text only works on small viewports.
- many
a
elements should bebutton
s - also some
div
s should bea
orbutton
<kiss-navlist>
should be<nav>
- icon links need
aria-hidden
- over-use of
aria-label
- use semantic html instead
defining custom paths/folder patterns
Using custom paths is useful to e. g. move the storage and the config folder above the webroot for further security. Maybe the .htaccess
files is not copied after restoring a backup (these damn dot files and shell expansion). Maybe some server config sucks and suddenly the .htaccess
is ignored and php files are printed as text/plain
(more a “change crap web hoster immediately” problem, but I saw these things a few times).
In v1 we had a defines.php
to set constants with custom file and folder paths. If I want to use custom paths now I have to write a custom index.php
, instantiate Cockpit
with custom config and add a modified Async
helper class to reflect the custom config. This feels very fragile.
hiding security updates in “cleanup” commit messages
This one got much better in v2 than in v1 with the introduction of CHANGELOG.md
. I actually read (or skim) all commits and it would be nice to be able to skip the ones, that are just named “cleanup”. Not a deal breaker, but annoying.
ui framework(s)
The php based templating with Lexy
in combination with riot.js
was quite intuitive from reading the code.
The switch from riot
to vue
feels like a really good decision. It is very well documented, which makes it easy to understand the source code or to write a new addon.
I never liked UiKit (very bloated), but I could always search for uikit v2 {feature}
to get a basic idea of all the fancy css classes and data atrributes.
Now there is kiss
, which might translate to “Keep It Short and Simple”. It’s definitely better than UiKit, but undocumented. It’s also not clear, which components are part of kiss, custom components, vue components or some vendor lib inside App
or System
module. It might be simple, but it is actually hard to use.
no php template engine anymore
LimeExtra\App
withLexy
from v1 was simple, but powerful enough- now I have to use plain php to escape variables or I have to hack into the core controller to overwrite the default render method to pass in a custom renderer (e. g. modified Lexy, Blade or Twig)
Using Lexy in v1 was also the reason, why no helper functions in the global scope were needed (e. g. @lang()
instead of t()
).
structure of core modules is not intuitive
Everytime I search for a file (e. g. Controller, Helper class or a component file), I have to look through many sub folders in the App
and System
module folders. I don’t understand, why they are separated. Even in the i18n handling, both modules are merged. It would be much cleaner, to drop the System module and merge it into the App module.
missing features
I’m not generally against paying for good software, but I’m not interested in proprietary software at all. So using the Pro features of Cockpit CMS v2 is not an option. To prevent accidentally violating copyright, I never read the source code of the Pro addons. But I assume, that they have the same accessibility problems like the core.
Also I can’t read the commit history of the Pro addons.
If I decide to use v2, I’ll probably reimplement the forms module from v1 with additions from my FormValidation addon. Because I care about accessibility now, I can’t really reuse many core features anymore. So this would be much more work, than I initially thought.
web console
I love the cli. Moving to Symfony\Console
was also a good idea. But the web console has copy/paste problems, no shell history and it is exposed without using an SSH key. This is an unnecessary attack vector. I could disable it with overwriting the web console controller. So again - no deal breaker, but annoying.
It should be a separate module/addon, like the Finder (which was hard to disable in v1 - also for security reasons).
community
- it never really felt like a community, more like a knowledge base or a FAQ section
- active users are gone (e. g. @serjoscha87, @pauloamgomes)
- information is cluttered
- v1 and v2 related questions on discourse without proper categorization
- v2 related questions on Github Discussions - This is just another Microsoft dependency. Also I don’t want to be logged in on Github all the time. SSH keys for git usage exist for a reason. I won’t participate much over there.
Conclusion
I’m still a big fan of the Lime micro framework, the event system and the modularity/hackability of Cockpit CMS.
I learned a lot about many core concepts for building a CMS, writing user interfaces and delivering performant frontends during the last years. Also my OOP skills and knowledge about PHP and JS libraries improved massively.
Now I’m not sure, if my main concerns could be addressed in the near future.
I also thought about using Cockpit (v1 or v2) as a library and writing a complete new user interface. In this case I would deactivate all features and implement one by one with progressive enhancement and accessibility in mind. This would be a massive amount of work.
In terms of modularity, accessibility and community Drupal seems to shine. After some research, the event system is much more complicated, it uses SQL, it’s not performance optimized, it still requires jQuery, RAM usage seems quite high (server and client side) and the default footprint is about 100MB (vs. 26MB of CP v2 or 19MB of CP v1). Maybe I get more comfortable with it during the next weeks…
After spending so much time with Cockpit, reading and contributing code, helping in the forum, reporting many security issues in private chats and writing multiple addons, it feels hard and wrong to leave this project. But I don’t feel comfortible with v2 for months now. Instead of spending even more time, it might be better to move forward and to learn about some interesting concepts from other content management systems…
I would be happy about opinions - similar experiences or why it’s still worth believing in Cockpit.
Also suggestions, which other CMS might be worth testing (open source, modular, accessible, PHP or JS based) are appreciated.