New stats section
Turn your dashboard into a smart report
Easy as 1-2-3
stats:
type: stats
reports:
- label: Revenue
value: "€29,682"
info: +112.5%
link: https://awesome.shop
theme: positive
- label: Transactions
value: "{{ page.transactions }}"
info: "{{ page.transactionsIncrease }}"
...
New table layout
Who said spreadsheets cannot be cool?
At a glance
notes:
type: pages
layout: table
columns:
subheading:
label: Subtitle
date:
label: Date
type: date
display: DD.MM.YYYY
tags:
label: Tags
type: tags
New system view
Everything system related at a glance
Environment
Security
Plugins
New toggles field
Stylish options you will love
toggles:
type: toggles
label: Labels
options:
# Short version with value: text
left: Left
center: Center
right: Right
justify: Justify
toggles:
type: toggles
label: Icons & Labels
options:
# Detailed version with more options
- value: left
text: Left
icon: text-left
- value: center
text: Center
icon: text-center
- value: right
text: Right
icon: text-right
- value: justify
text: Justify
icon: text-justify
toggles:
type: toggles
label: Icons
labels: false
options:
# Text will be used as title
# attribute for the <label>
- value: left
text: Left
icon: text-left
- value: center
text: Center
icon: text-center
- value: right
text: Right
icon: text-right
- value: justify
text: Justify
icon: text-justify
toggles:
type: toggles
label: Icons
labels: false
grow: false
options:
- value: left
text: Left
icon: text-left
- value: center
text: Center
icon: text-center
- value: right
text: Right
icon: text-right
- value: justify
text: Justify
icon: text-justify
New section search
Speedy filtering of pages and files
sections:
pages:
type: pages
search: true
layout: cards
Refined UI
A fresh new look for your Panel
Fresh & familiar
New icons
- archive
- badge
- brush
- discord
- discount
- folder-structure
- github
- money
- palette
- pen
- store
- ticket
New file icons
Changelog
Features
- Brand new statistics section
- New search option for pages and files sections https://kirby.nolt.io/14 #4316
- New field previews in tables for the tags, radio, select, multiselect, checkboxes, structure, blocks, layout, files, pages and users fields. #4294 #4195
- New system health checks to detect unintentionally exposed parts of your installation (Git repo, content folder, site folder, kirby folder)
$request->auth()now also handles Kirby'sSessionauth type automatically #4264- The auth types for
$request->auth()can now be extended by plugins #4264 - New
$kirby->response()->usesAuth()and->usesCookie()method to tell Kirby's automatic caching system about usedAuthorizationheaders and cookies. These methods are called automatically when the KirbyRequestandCookieclasses are used. - New
$kirby->request()->hasAuth()method to check the existence of theAuthorizationheader #4277 - New
$kirby->isNativeComponent($component)method #4348 - New
k-tablecomponent to create consistent tables easily - Styled preview for tags in tables
- New JS helper
$helpers.object.isEmpty(value)
New Str methods
Str::afterStart() and Str::beforeEnd()
… to remove a substring only from the start or end of a string #4339
Str::camel() and Str::studly()
… for case-altering of strings #4338
echo Str::camel('foo_bar'); //=> fooBar
echo Str::studly('foo_bar'); //=> FooBar
Str::increment()
… for appending a number -1 to a string or incrementing the ending number to allow -2, -3, etc. #4340
echo Str::increment('Page-1'); //=> Page-2
echo Str::increment('Page', '_'); //=> Page_1
Str::wrap()
… for wrapping the string with the given strings.
echo Str::wrap($string, '# ', ' {.title}'); //=> # Post title {.title}
echo Str::wrap($string, '"'); //=> "Post title"
New validators
V::json()
… to validate correctly formatted JSON strings.
if (V::json('{"foo": "Foo"}')) {
// do something...
}
V::empty() and V::notEmpty()
To check for empty values in PHP is not as easy as it sounds like. empty() is often giving wrong results. Especially if you pass 0 or '0'. The new validators help with this task. They also serve as underlying logic for a better V::required()validator. It can now be used in two ways:
With a reference array …
V::required('myField', ['myField' => 'Some value']);
or just a value …
V::required('My value');
This makes the method more versatile and it can be used in V::value():
V::value('My value', [
'required' => true,
'minlength' => 5,
]);
Enhancements
Custom helpers
Helpers can now be deactivated by setting a global constant named KIRBY_HELPER_*, e.g. KIRBY_HELPER_DUMP to false #4018 Check out the documents.
In some cases, our global helper functions will collide with other tools' global functions if they use the same name, e.g. dump(). If you are affected by this problem, you can now set the matching constant, e.g. KIRBY_HELPER_DUMP, to false and Kirby will not register its global helper.
To ensure that Kirby still functions properly, we moved all functionality from the helper functions to class methods (and the global helper functions are only aliases now). We recommend that plugin developers also start using these class methods to ensure that your code always uses Kirby's core helpers:
- New
Cms\App::csrf()method with the functionality of thecsrf()helper - New
Cms\App::image()method with the functionality of theimage()helper - New
Cms\Helpers::deprecated()method with the functionality of thedeprecated()helper - New
Cms\Helpers::size()method with the functionality of thesize()helper - New
Cms\Helpers::dump()method with the functionality of thedump()helper - New
Cms\Html::css()method with the functionality of thecss()helper - New
Cms\Html::js()method with the functionality of thejs()helper - New
Cms\Html::svg()method with the functionality of thesvg()helper - New
Filesystem\F::loadClasses()method with the functionality of theload()helper - New
Http\Router::execute()method with the functionality of therouter()helper - New
Http\Response::go()method with the functionality of thego()helper https://kirby.nolt.io/369 - New
Toolkit\Date::roundedTimestamp()method with the functionality of thetimestamp()helper - New
Toolkit\Str::esc()method with the functionality of theesc()helper - New
Toolkit\Str::uuid()method with the functionality of theuuid()helper - New
Toolkit\V::invalid()method with the functionality of theinvalid()helper - Extended
Cms\App::kirbytag()method with the functionality of thekirbytag()helper. Allows to pass an array as first parameter. - Extended
Cms\App::snippet()method with the functionality of thesnippet()helper. - Extended
Toolkit\Html::attr()method with the functionality of theattr()helper. Allows to pass$beforeand$afterstring.
Core
- Added Kirby’s PHP and extension dependencies to
composer.json#4216 Toolkit\Xml::value()now uses better logic to determine whether aCDATAblock is needed. #4335- Methods in Kirby's core now use the global environment object instead of
$_SERVERwherever possible - JSON errors in debug mode now contain the file path relative to the document root for consistency with the API that already handles it like this.
- Kirby's response handling now automatically controls page and HTTP caching based on cookies, session and the
Authorizationrequest header used by the response. If any of these are used and also contained in the request, the response is considered private. If they are used but not contained in the request, the page is only cached for visitors without these cookies/session/auth. Manual sessions are always considered private. #3976 $pages->find()can now find pages by their translated URI in secondary languages of multi-lang setups, even in collections without a parent (like$page->grandChildren())
Panel
Bug fixes
Core
- Fixed return type hints to include
nullas possible return value forCms\Pagination::firstPageUrl()
andCms\Pagination::lastPageUrl()#4314 - Fixed a bug where field content was copied to secondary languages even though
translate: falsewas set #2577 - Fixed a bug where fields with uppercase field names in virtual pages could not be searched #4142
- File permissions of all non-executable files in the Kirby repo have been set to
644. #4336 - Editing a page title before the translation file had been created caused untranslatable fields to be copied to the translation file #2789
kirby->url()no longer adds the wrong port or protocol on nginx / reverse proxy #4311- Url overwrite config now also overwrites protocol correctly #4234
file::urlcomponent no longer breaks the preview URL in the Panel #4143- Fixed a regression that prevented page and HTTP caching when the session was accessed, even when the session was not active #3976
- The behavior of the
$pages->find(),$pages->findById()and$pages->findByUri()methods in non-direct-child collections (e.g.$page->grandChildren()) is now consistent between single-lang and multi-lang installations #4105
Panel
k-emptynow actually supports itstextprop- Improved structure field columns on mobile #3638
- Structure field sticky header inside drawers #4242
- Fixed wrong draggable ghost element for structure field rows #4242
- Preview image from other page was not shown when the page is a child of site and status
publishedwas queried #4297 - Uploading multiple files now adds correct sorting numbers #4317
- Better responsiveness for Panel section headers #4316
- Fixed Copy & Paste in list blocks on the first try #3974
- The Panel and API now send the
Cache-Control: no-store, privateresponse header instead of justCache-Control: no-storefor better compatibility with caches like CloudFlare #4299
Refactoring
New Http\Environment class
The new object-oriented Http\Environment class replaces the former static Http\Server class and takes over all its methods.
It takes care of detecting all necessary parts (host, port, https, etc.) and considers an optional allow list of URLs to validate the current environment:
$env = new Environment([
'allowed' => [
'https://dev.getkirby.com',
'https://test.getkirby.com:9999/subfolder'
]
]);
I.e. if it finds to be running on the test.getkirby.com host from above, https is on and the port is 9999 and the subfolder also matches, you can get all those parts now correctly via:
$env->https();
$env->host();
$env->subfolder();
$env->port();
We use this new class as foundational element in our $kirby instance to construct absolute and relative URLs, roots and paths. You can access it and its methods with $kirby->environment(). Kirby internally uses the Environment class to set the site's base URL based on the url option.
Core
- Improved internal usage of router callbacks #4260
- The Kirby core no longer uses Kirby's helper functions internally but calls the underlying methods directly. #4018
- Content field names are now always handled case-insensitively.
- Kirby's
composer.jsonno longer pins thepsr/loglibrary to a specific version #4392
Panel
Deprecated
- The
dumpcomponent was deprecated and superseded by the new feature to replace helpers (KIRBY_HELPER_DUMPconstant). The component will be removed in Kirby 3.8.0. #4018 Http\Serverclass will be removed in 3.8.0- The constants in
Http\Server::are now deprecated and will be removed in 3.8.0. They are no longer needed. - The
$pages->findById()and$pages->findByUri()methods have been deprecated and will be removed in 3.8.0. If you want to directly get a page by ID, use$pages->get(). If you want the previous fuzzy behaviour that queries both by ID and URI, use$pages->find(). - The
$pages->findByIdRecursive()method has been deprecated and will be removed in v3.8.0. Please use$pages->find()instead. - The
$files->findById()method has been deprecated and will be removed in v3.8.0. Please use$files->find()instead. - The
$pages->findByKey(),$files->findByKey()and$users->findByKey()methods have been marked as internal. Please use thefind()methods instead. - The
$inlineparameter in$kirby->kirbytext()is deprecated. Use$options['markdown']['inline']instead. - The
$inlineparameter in$kirby->markdown()is deprecated. Use$options['inline']instead. - Blueprint presets
page,pagesandfilesare deprecated.
Deprecation warnings
The following deprecated methods/parts throw warnings in 3.7.0 and will be removed starting in 3.8.0 #4335
Methods
| Deprecated | Use instead | Removed in |
|---|---|---|
| $file->dragText() | $file->panel()->dragText() | 3.8 |
| $file->panelIcon() | $file->panel()->icon() | 3.8 |
| $file->panelImage() | $file->panel()->image() | 3.8 |
| $file->panelOptions() | $file->panel()->options() | 3.8 |
| $file->panelPath() | $file->panel()->path() | 3.8 |
| $file->panelUrl() | $file->panel()->url() | 3.8 |
| $file->pickerData() | $file->panel()->pickerData() | 3.8 |
| $files->findById() | $files->find() | 3.8 |
| $kirby->server() | $kirby->environment() | 3.9 |
| $page->dragText() | $page->panel()->dragText() | 3.8 |
| $page->panelIcon() | $page->panel()->icon() | 3.8 |
| $page->panelId() | $page->panel()->id() | 3.8 |
| $page->panelImage() | $page->panel()->image() | 3.8 |
| $page->panelOptions() | $page->panel()->options() | 3.8 |
| $page->panelPath() | $page->panel()->path() | 3.8 |
| $page->panelUrl() | $page->panel()->url() | 3.8 |
| $page->pickerData() | $page->panel()->pickerData() | 3.8 |
| $pages->findById() | $pages->find() | 3.8 |
| $pages->findByIdRecursive() | $pages->find() | 3.8 |
| $pages->findByUri() | $pages->find() | 3.8 |
| $site->panelIcon() | $site->panel()->icon() | 3.8 |
| $site->panelImage() | $site->panel()->image() | 3.8 |
| $site->panelOptions() | $site->panel()->options() | 3.8 |
| $site->panelPath() | $site->panel()->path() | 3.8 |
| $site->panelUrl() | $site->panel()->url() | 3.8 |
| $site->pickerData() | $site->panel()->pickerData() | 3.8 |
| $user->panelIcon() | $user->panel()->icon() | 3.8 |
| $user->panelImage() | $user->panel()->image() | 3.8 |
| $user->panelOptions() | $user->panel()->options() | 3.8 |
| $user->panelPath() | $user->panel()->path() | 3.8 |
| $user->panelUrl() | $user->panel()->url() | 3.8 |
| $user->pickerData() | $user->panel()->pickerData() | 3.8 |
| Panel\Document::customCss() | Panel\Document::customAsset('panel.css') | 3.8 |
| Panel\Document::customJs() | Panel\Document::customAsset('panel.js') | 3.8 |
API
| Deprecated | Use instead | Removed in |
|---|---|---|
| API field page.next | - | 3.8 |
| API field page.prev | - | 3.8 |
Breaking changes
We try to avoid breaking changes as much as we can. But we also put a lot of effort in keeping our technical debt in Kirby as low as possible. Sometimes such breaking changes are necessary to move forward with a clean code base.
You might wonder why there are breaking changes in a minor release according to Semantic Versioning.
We stick to the following versioning scheme:
{generation}.{major}.{minor}(.{patch})
This release is Kirby 3.7.0.0 (major release 7 of Kirby 3)
Traditionally, we combine patch and minor releases though and only need the fourth versioning level for regression fixes.
Composer installation
If you install Kirby using Composer, you may run into errors if your command line installation of PHP doesn’t have the extensions installed that Kirby requires. If you are sure that your web server fulfills the requirements, you can use Composer’s --ignore-platform-reqs or --ignore-platform-req=ext-* flags. #4216
Core
Http\Router::$beforeEachandHttp\Router::$afterEacharen't static anymore. Pass them in an array as second argument to the constructor instead. #4260- Custom auth type classes in the
Kirby\Http\Request\Auth\namespace now need to be registered in theKirby\Http\Request::$authTypesarray. We also recommend to move the classes to your own namespace to avoid interference with core classes. #4264 - Field names of virtual pages' content are now converted to lowercase. If your virtual page has two fields that only differ in capitalization, only the last defined one will be available.
- The
Server::hosts()method was removed. Please set the allowed URLs via theurloption. pagehelper now always only returns aKirby\Cms\Pageobject ornull, never a pages collection. Only allows passing a single id as parameter. #4335pageshelper now always only returns aKirby\Cms\Pagescollection ornull, never single page object. #4335- Creating a
Kirby\Cms\Fileobject requires theparentproperty now. #4335 Toolkit\Str::toBytes()strictly only accepts a string as parameter now. #4335- The
$pages->children(),$pages->drafts()and$pages->index()methods no longer set the$pages->parent()object as the collection items can have multiple different parents. This can change the behaviour when finding collection items in secondary languages and when merging collections. #4105 - The
dump()ande()helper function checks have been removed. If your plugins or dependencies have functions with these two names, you will get an error. You can override the conflicting functions by defining theKIRBY_HELPER_*contants. Check out how to do it. - The second argument of the
kirbytextinline()andkti()helpers has been renamed from$datato$options
Removed methods
The following deprecated methods/parts have been removed #4335
| Removed | Use instead |
|---|---|
| Cms\App::setLocale() | Toolkit\Locale::set() |
| Cms\Block::_key() | Cms\Block::type() |
| Cms\Block::_uid() | Cms\Block::id() |
| Toolkit\I18n::fallback() | Toolkit\I18n::fallbacks() |
| Toolkit\Str::template(): $fallback, $start and $end parameters | pass an array to the $options parameter |
Panel
k-block-type-tableandk-structure-fieldhave been largely refactored. If you (or a plugin) extends these, stuff might break.k-pages-sectionandk-files-sectionhave been modified extensively #4247Toolkit\Html::attr([], null)now consistently returnsnullinstead of an empty string. #4018- Removed
builderandeditorfield migration for blocks field.
API
The following deprecated methods/parts have been removed #4335
| Removed | Use instead |
|---|---|
| API fields page.panelIcon / file.panelIcon | page.panelImage / file.panelImage |
| GET (:all)/lock API endpoint | - |
| GET (:all)/unlock API endpoint | - |
| GET pages/(:any)/children/blueprints API endpoint | GET pages/(:any)/blueprints |
| GET site/children/blueprints API endpoint | GET site/blueprints |
| JS API method files.rename() | files.changeName() |
| JS API method pages.slug() | pages.changeSlug() |
| JS API method pages.status() | pages.changeStatus() |
| JS API method pages.template() | pages.changeTemplate() |
| JS API method pages.title() | pages.changeTitle() |
| JS API method site.title() | site.changeTitle() |
| JS API method system.info() | system.get() |