KS Projects WordPress Plugin

Task Take the portfolio archive out of the theme, so replacing the theme cannot take the work with it — and publish the result for anyone else building a portfolio on WordPress.

A portfolio lives in the theme until the day the theme is replaced — and then the post type, the tags and every case study go with it. KS Projects takes the archive out of the theme and gives it back as its own content type, published as open source under GPLv3.

Source, issues and releases on GitHub

The split it is built around

The plugin owns the data and the editing screens. The theme owns how any of it looks. That line is the whole design: a redesign replaces the theme and the archive does not notice.

So the plugin ships a plain default for each of the three project screens, and a theme takes any of them over simply by having a file of the same name. There is no setting to switch and nothing to deregister — the check is the same one WordPress already uses to find a template anywhere else.

Those defaults carry no framework classes on purpose. A default that arrived with a design would look wrong on every theme except the one it came from.

A case study is more than a post

Two metaboxes carry what a portfolio entry actually needs. The card holds the short name, a subtitle and the address the work went live at — a portfolio title tends to be a sentence, and a grid needs a name.

The credits hold what the client asked for, who worked on it and what shipped. Written as hand-made HTML inside the post content, which is where that block usually lives, it has to be edited by hand on every project the first time the design changes. Held as fields it is the theme's to lay out, and all three keys are opted into post revisions, which meta is not by default.

A template per project

One case study wants a full-bleed gallery, another a long read. A project may name a PHP file in the theme, and that file renders in place of the post content and nothing else — the heading, the tags and the credits stay with the theme. A template is a body, not a page: no duplicated chrome, and a change to the frame reaches every project at once.

The page you are reading is that feature in use.

The chooser sits on the editing screen and in Quick Edit, and the projects list gains a column, so which projects have their own layout is answerable at a glance. Nothing is written to the post content and nothing is deleted: turning the template off brings the editor's version straight back, and a file that goes missing falls back to it rather than rendering an empty page.

What is deliberately not a setting

The URL slugs are editable. The post type and taxonomy names are not, and that is the more interesting half.

Those names are the strings every row in the database is filed against. Offering them in a settings screen would offer a one-click way to lose an archive: change one on a site that already has projects and every existing entry stays filed under a type nobody registers any more. They disappear from the admin list, and their addresses stop resolving. The names can still be set from the site configuration file — before the first project exists, and never after.

How it is put together

Namespaced classes with modern autoloading, and a hook declared on the method that answers it rather than in a registration list somewhere else — a renamed method cannot leave a registration pointing at nothing. There is no compiled cache behind that and no directory scan: the services are listed by hand in one file, so there is one obvious place that says what the plugin consists of.

The API a theme calls is one class with one import, not a scattering of global functions.

No runtime dependencies at all, so the repository runs the moment it is cloned. Composer is there for the development tooling only, installed apart from the plugin so a test class can never reach the autoloader that ships.

What runs before a release

Static analysis at the strictest level the tool offers, coding standards against the WordPress ruleset with every departure recorded next to its reason, and unit tests over the parts that can be reasoned about on their own — the slug rules, the meta key set, the featured list.

Two checks exist because their failure is silent otherwise: one refuses a commit whose shipped autoloader disagrees with the source, and one refuses a translation that was edited but never compiled.

Roles

Developer
Konstantin Sorokin

Work done
  • Post type, taxonomy and the two case-study metaboxes
  • Default templates a theme overrides by name
  • Per-project templates, chosen on the post or in Quick Edit
  • Settings screen with slug validation and a deferred rewrite flush
  • Russian and Ukrainian translations
  • PHPStan level 8, WordPress coding standards, unit tests, CI