Initial commit

This commit is contained in:
Paul Nicoué 2022-06-17 17:51:59 +02:00
commit 73c6b816c0
716 changed files with 170045 additions and 0 deletions

49
.gitignore vendored Normal file
View file

@ -0,0 +1,49 @@
# --------------------------------------------------
# GENERAL
# --------------------------------------------------
# Content
/content/
# Dependencies
/vendor/
# Editors
*.sublime-project
*.sublime-workspace
/.vscode
/.idea
# Plugins
/site/plugins/
# System files
Icon
.DS_Store
# Temporary files
/media/*
!/media/index.html
# --------------------------------------------------
# SECURITY
# --------------------------------------------------
# Accounts
/site/accounts/*
!/site/accounts/index.html
# Cache Files
/site/cache/*
!/site/cache/index.html
# Configuration files
/site/config/config.xiaowang.fr.php
/site/config/config.xiaowang.test.php
# License
/site/config/.license
# Sessions
/site/sessions/*
!/site/sessions/index.html

57
.htaccess Normal file
View file

@ -0,0 +1,57 @@
# Kirby .htaccess
# revision 2020-06-15
# rewrite rules
<IfModule mod_rewrite.c>
# enable awesome urls. i.e.:
# http://yourdomain.com/about-us/team
RewriteEngine on
# make sure to set the RewriteBase correctly
# if you are running the site in a subfolder;
# otherwise links or the entire site will break.
#
# If your homepage is http://yourdomain.com/mysite,
# set the RewriteBase to:
#
# RewriteBase /mysite
# In some environments it's necessary to
# set the RewriteBase to:
#
# RewriteBase /
# block files and folders beginning with a dot, such as .git
# except for the .well-known folder, which is used for Let's Encrypt and security.txt
RewriteRule (^|/)\.(?!well-known\/) index.php [L]
# block all files in the content folder from being accessed directly
RewriteRule ^content/(.*) index.php [L]
# block all files in the site folder from being accessed directly
RewriteRule ^site/(.*) index.php [L]
# block direct access to Kirby and the Panel sources
RewriteRule ^kirby/(.*) index.php [L]
# make site links work
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*) index.php [L]
</IfModule>
# pass the Authorization header to PHP
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
# compress text file responses
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
</IfModule>

1
assets/css/panel.min.css vendored Normal file
View file

@ -0,0 +1 @@
.k-textarea-field .k-toolbar .k-dropdown .k-button:nth-of-type(2),.k-textarea-field .k-toolbar .k-dropdown .k-button:nth-of-type(3){display:none}.kirby-imagecrop-field .k-column:nth-of-type(2){display:none}/*# sourceMappingURL=panel.min.css.map */

View file

@ -0,0 +1 @@
{"version":3,"sourceRoot":"","sources":["panel.scss"],"names":[],"mappings":"AAYY,oIAEI,aAUZ,gDACI","file":"panel.min.css"}

28
assets/css/panel.scss Normal file
View file

@ -0,0 +1,28 @@
// ----------------------------------------------------------------------------
// KIRBY PANEL CUSTOMIZATION
// ----------------------------------------------------------------------------
// Textarea headline buttons
.k-textarea-field {
.k-toolbar {
.k-dropdown {
.k-button:nth-of-type(2),
.k-button:nth-of-type(3) {
display: none;
}
}
}
}
// Visual image crop field properties
.kirby-imagecrop-field {
.k-column:nth-of-type(2) {
display: none;
}
}

View file

@ -0,0 +1,3 @@
// ----------------------------------------------------------------------------
// ANIMATIONS
// ----------------------------------------------------------------------------

View file

@ -0,0 +1,3 @@
// ----------------------------------------------------------------------------
// FONTS
// ----------------------------------------------------------------------------

View file

@ -0,0 +1,78 @@
/* ----------------------------------------------------------------------------
MINIRESET V0.0.6
---------------------------------------------------------------------------- */
html,
body,
p,
ol,
ul,
li,
dl,
dt,
dd,
blockquote,
figure,
fieldset,
legend,
textarea,
pre,
iframe,
hr,
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
padding: 0;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: 100%;
font-weight: normal;
}
ul {
list-style: none;
}
button,
input,
select {
margin: 0;
}
html {
box-sizing: border-box;
}
*, *::before, *::after {
box-sizing: inherit;
}
img,
video {
height: auto;
max-width: 100%;
}
iframe {
border: 0;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
td,
th {
padding: 0;
}

View file

@ -0,0 +1,29 @@
// ----------------------------------------------------------------------------
// VARIABLES
// ----------------------------------------------------------------------------
:root {
// Fonts
// Dimensions
// Colors
}
// Media queries
$tablet-media-query: 48rem;
$desktop-media-query: 62rem;
@media screen and (min-width: $tablet-media-query) {
:root {
// Fonts
// Dimensions
}
}

32
assets/css/style.scss Normal file
View file

@ -0,0 +1,32 @@
@use 'partials/minireset';
@use 'partials/fonts';
@use 'partials/variables' as *;
@use 'partials/animations';
// ----------------------------------------------------------------------------
// GENERALITIES
// ----------------------------------------------------------------------------
// Fonts and colors
// Link style
// General grid layout
// ----------------------------------------------------------------------------
// HEADER
// ----------------------------------------------------------------------------
// Header bar
// ----------------------------------------------------------------------------
// MAIN
// ----------------------------------------------------------------------------
// Home section
// ----------------------------------------------------------------------------
// FOOTER
// ----------------------------------------------------------------------------
// Footer bar

26
assets/js/app.js Normal file
View file

@ -0,0 +1,26 @@
'use strict';
// ----------------------------------------------------------------------------
// DATA
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// UTILS
// ----------------------------------------------------------------------------
// Convert rem to pixels by getting font-size CSS property
function convertRemToPixels(rem) {
let fontSize = parseFloat(window.getComputedStyle(document.body).getPropertyValue('font-size'));
return rem * fontSize;
}
// ----------------------------------------------------------------------------
// LOGIC
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// PROGRAM
// ----------------------------------------------------------------------------
// Enable CSS :active pseudo-class in Safari Mobile
document.addEventListener("touchstart", function() {},false);

30
composer.json Normal file
View file

@ -0,0 +1,30 @@
{
"name": "paulnicoue/julienmonnerie",
"description": "Julien Monnerie",
"type": "project",
"homepage": "https://julienmonnerie.com",
"authors": [
{
"name": "Paul Nicoué",
"email": "contact@paulnicoue.com",
"homepage": "https://paulnicoue.com"
}
],
"require": {
"php": ">=7.3.0 <8.1.0",
"getkirby/cms": "^3.5",
"amteich/kirby-twig": "^4.1",
"sylvainjule/matomo": "^1.0",
"kirbyzone/sitemapper": "^1.2",
"mullema/k3-image-clip": "^3.0"
},
"scripts": {
"start": [
"Composer\\Config::disableProcessTimeout",
"@php -S localhost:8000 kirby/router.php"
]
},
"config": {
"optimize-autoloader": true
}
}

1142
composer.lock generated Normal file

File diff suppressed because it is too large Load diff

5
index.php Normal file
View file

@ -0,0 +1,5 @@
<?php
require 'kirby/bootstrap.php';
echo (new Kirby)->render();

15
kirby/.editorconfig Normal file
View file

@ -0,0 +1,15 @@
# This file is for unifying the coding style for different editors and IDEs
# editorconfig.org
# PHP PSR-12 Coding Standards
# https://www.php-fig.org/psr/psr-12/
root = true
[*.php]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4

6
kirby/.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,6 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
}

4
kirby/.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,4 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
}

99
kirby/CONTRIBUTING.md Normal file
View file

@ -0,0 +1,99 @@
# Contributing
:+1::tada: First off, yes, you can contribute and thanks already for taking the time if you do! :tada::+1:
## How we organize code
To keep track of different states of our code (current release, bugfixes, features) we use branches:
| Branch | Used for | PRs allowed? |
| ----------- | ------------------------------------------------------------------------ | --------------------------- |
| `main` | Latest released version | - |
| `develop` | Working branch for next release, e.g. `3.7.x` | ✅ |
| `fix/*` | Temporary branches for single patch | - |
| `feature/*` | Temporary branches for single feature | - |
| `release/*` | Pre-releases in testing before they are merged into `main` when released | only during release testing |
We will review all pull requests (PRs) to `develop` and merge them if accepted, once an appropriate version is upcoming. Please understand that this might not be the immediate next release and might take some time.
## How you can contribute
### Report a bug
When you find a bug, the first step to fixing it is to help us understand and reproduce the bug as best as possible. When you create a bug report, please include as many details as possible. Fill out [the template](ISSUE_TEMPLATE/bug_report.md) because the requested information helps us resolve issues so much faster.
### Bug fixes
For bug fixes, please create a new branch following the name scheme: `fix/issue_number-bug-x`, e.g. `fix/234-this-nasty-bug`. Limit bug fix PRs to a single bug. **Do not mix multiple bug fixes in a single PR.** This will make it easier for us to review the fix and merge it.
- Always send bug fix PRs against the `develop` branchnot `main`.
- Add a helpful description of what the PR does if it is not 100% self-explanatory.
- Every bug fix should include a [unit test](#tests) to avoid future regressions. Let us know if you need help with that.
- Make sure your code [style](#style) matches ours and includes [comments/in-code documentation](#documentation).
- Make sure your branch is up to date with the latest state on the `develop` branch. [Rebase](https://help.github.com/articles/about-pull-request-merges/) changes before you send the PR.
### Features
For features create a new branch following the name scheme: `feature/issue_number-feature-x`, e.g. `feature/123-awesome-function`. Our [feedback platform](https://feedback.getkirby.com) can be a good source of highly requested features. Maybe your feature idea already exists and you can get valuable feedback from other Kirby users. Focus on a single feature per PR. Don't mix features!
- Always send feature PRs against the `develop` branchnot `main`.
- Add a helpful description of what the PR does.
- New features should include [unit tests](#tests). Let us know if you need help with that.
- Make your code [style](#style) matches ours and includes [comments/in-code documentation](#documentation).
- Make sure your branch is up to date with the latest state on the `develop` branch. [Rebase](https://help.github.com/articles/about-pull-request-merges/) changes before you send the PR.
We try to bundle features in our major releases, e.g. `3.x`. That is why we might only review and, if accepted, merge your PR once an appropriate release is upcoming. Please understand that we cannot merge all feature ideas or that it might take a while. Check out the [roadmap](https://roadmap.getkirby.com) to see upcoming releases.
### Translations
We are really happy about any help with translations. Please do not directly translate JSON files, though. We use a service called Transifex to handle [all translations](https://translation.getkirby.com/). Create an account there and send us a request to join our translator group. Additionally, also send an email to <support@getkirby.com>. Unfortunately, we don't get notified properly about new translator requests.
## How we write code
### Style
#### Backend (PHP)
We use [PHP CS Fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer) to ensure a consistent style for our PHP code. It is mainly based on [PSR-12](https://www.php-fig.org/psr/psr-12/). [Install PHP CS Fixer globally](https://github.com/FriendsOfPHP/PHP-CS-Fixer#globally-composer) via Composer and then run `composer fix` in the `kirby` folder to check for inconsistencies and fix them. Our automated PR checks will fail if there are code style issues with your code.
#### Frontend/Panel (JavaScript, Vue)
We use [Prettier](https://prettier.io) to ensure a consistent style for our JavaScript and Vue code. After running `npm install` in the `kirby/panel` folder, you can run `npm run format` to check for inconsistencies and fix them. We also use [ESLint](https://eslint.org) which you can use by running `npm run lint` and/or `npm run lint:fix`.
### Documentation
In-code documentation and comments help us understand each other's code - or our own code after some months. Especially when matters get more complicated, we try to add a lot of comments to explain what the code does or why we implemented it like this. Even better than good comments is good code that is easy to understand.
#### Backend (PHP)
We use PHP [DocBlocks](https://docs.phpdoc.org/guide/references/phpdoc/basic-syntax.html#what-is-a-docblock) for classes and methods.
#### Frontend/Panel (JavaScript, Vue)
We use [JSDoc](https://jsdoc.app) for documenting JavaScript code, especially for [Vue components](https://vue-styleguidist.github.io/docs/Documenting.html).
#### Public documentation
We also document Kirby on the Kirby website at <https://getkirby.com>. However we recommend to wait with writing public documentation until the feature PR is merged. If you don't know where the documentation for a feature best belongs, don't worry. We can take care of writing the docs.
### Tests
Unit and integration tests help us prevent regressions when we make changes to the code. Every bug fix should also add a unit test for the fixed bug to make sure we won't re-introduce the same problem later down the road. Every new feature should be accompanied by unit tests to protect it from breaking through future changes.
#### Backend (PHP)
We use [PHPUnit](https://phpunit.de) for unit test for our PHP code. You can find all existing tests in the [`kirby/tests` subfolders](https://github.com/getkirby/kirby/tree/main/tests). Take a look to see how we usually structure our tests.
#### Frontend/Panel (JavaScript, Vue)
The Panel doesn't have extensive test coverage yet. That's an area we are still trying to improve.
We use [vitest](https://vitest.dev) for unit tests for JavaScript and Vue components - `.test.js` files next to the actual JavaScript/Vue file.
For integration tests, we use [cypress](https://www.cypress.io) - `.e2e.js` files.
## And last…
Let us know [in the forum](https://forum.getkirby.com) if you have questions.
**And once more: thank you!** :+1::tada:

211
kirby/LICENSE.md Normal file
View file

@ -0,0 +1,211 @@
# Kirby License Agreement
Published: March 15, 2022
Source: https://getkirby.com/license/2022-03-15
## About this Agreement
While Kirby's source code is publicly available, Kirby is **not free**. To use Kirby in production, you need to [purchase a license](https://getkirby.com/buy).
This End User License Agreement (the **"Agreement"**) is fundamental to the relationship between you and us. Therefore we recommend to read this Agreement carefully before you download, install or use Kirby.
If you do not agree to this Agreement, please do not download, install or use Kirby. Installation or use of Kirby signifies that you have read, understood, and agreed to be bound by this Agreement.
## Definitions
Before we get started with the conditions of the Agreement, let's define the terms that will be used throughout it:
- When we refer to **"You"**, we mean the licensee. Before purchasing Kirby, that's the individual or company that has downloaded and/or installed Kirby for a Development Installation, Private Installation or Extension Demo. When used for a Public Site, the licensee is the individual or company that has purchased the Kirby license. If you work on a client project and have purchased the Kirby license for your client, you (and _not_ the client) are the licensee.
- When we refer to **"We"**/**"Us"**/**"Our"**, we mean the licensor, the Content Folder GmbH & Co. KG. You can find Our company and contact information on Our [contact page](https://getkirby.com/contact).
- A **"Website"** is a single Kirby project that is defined by its domain name and root directory (e.g. `https://sub.example.com` or `https://example.com/example/`). Each (sub)domain and root directory is a separate Website, even if the projects are related in any way. Exception: If You use the cross-domain multi-language feature with the same `content` folder, these domains count as the same Website.
You may use Kirby as a headless backend or as a static site generator. In these cases the Website is defined by the domain and root directory of the user- or visitor-facing frontend(s).
- A **"Development Installation"** is a Website that is installed purely for the purposes of development and client preview. It must only be accessible by a restricted number of users (like on a personal computer, on a server in a network with restricted access or when protecting a staging website with a password that only a restricted number of users know).
- A **"Private Installation"** is a Website that is installed purely for personal use. It must only be accessible by You and Your family.
- An **"Extension Demo"** is a Website with the single purpose to showcase a free or commercial Kirby theme or Kirby plugin, as long as that Website only contains demo content. If the showcased extension is a Kirby theme, the demo content must be exactly as shipped with the theme. Demos for Kirby plugins may _not_ contain any additional content that is not needed to showcase the plugin in use.
- A **"Public Site"** is a Website that is _neither_ a Development Installation, a Private Installation nor an Extension Demo.
- An **"Update"** is defined as a Kirby release which adds smaller new features, minor functionality enhancements or bug fixes. This class of release is identified by the change of the revision to the right of the first decimal point, e.g. 3.1 to 3.2, 3.X.1 to 3.X.2 or 3.X.X.1 to 3.X.X.2.
- An **"Upgrade"** is a major Kirby release which incorporates major new features or enhancements that increase the core functionality of Kirby to a larger extent. This class of release is identified by the change of the revision to the left of the first decimal point, e.g. 3.X to 4.0.
- The **"Source Code"** is defined as the contents of all files that are provided with Kirby and that make Kirby work. This includes (but is not limited to) all PHP, JavaScript, JSON, HTML and CSS files as well as all related image and other media files.
Every time you see one of these capitalized terms in the following text, it has the meaning that has been explained above.
## Usage for a Public Site
Installing Kirby on or using it for a Public Site requires a [paid license](https://getkirby.com/buy).
As Kirby is software and software is intangible, We don't sell it as such. Instead, this Agreement grants a license for each purchase to install and use a single instance of Kirby on a **specific Website**. Additional Kirby licenses must be purchased in order to install and use Kirby on **additional Websites**.
The license is **non-exclusive** (meaning that You are not the only one who We will issue a license) and **generally non-transferable** (meaning that the one who purchases the license is the licensee).
On request, We will **transfer** a license to anyone who is also allowed to buy Kirby licenses by law and this Agreement.
We will also **reassign** a license to another Website domain and root directory of Your choice, provided that You confirm that the previous Website is no longer in operation and will not be operated with the same license in the future.
If you need to transfer your Kirby license to another individual or company (for example to your client or a new agency) or reassign it to a different project, please get in touch directly at <support@getkirby.com>.
A license is valid for all Updates of the same major Kirby release. We reserve the right to charge an **upgrade fee for Upgrade releases**. Whether a release is an Update or Upgrade is at Our sole discretion.
## Order Process
Our order process is conducted by Our online reseller [Paddle.com](https://paddle.com). Paddle.com is the Merchant of Record for all Our orders. Paddle provides all customer service inquiries and handles returns.
## Free Licenses
Kirby can be used **for free in the following cases**.
Please note that the restrictions and all other clauses of this Agreement also apply to free licenses. You may especially _not_ alter or circumvent the licensing features.
### Usage for a Development Installation
We believe that it should be possible to test and evaluate software before having to purchase a license. Also, We understand that a web project first needs to be built in a protected environment before it can be published.
Therefore, installing and using Kirby on a personal computer (like a desktop PC, notebook or tablet) or server for a Development Installation is **free** for as long as You need.
The usage of Kirby in production (with the intention to handle production data or content) is _never_ considered a Development Installation, even in internal apps or systems.
### Usage for a Private Installation
You may also install and use Kirby for **free** in Private Installations as long as they are not accessible by anyone except You and Your family.
Our [definition](#definitions) of a Private Installation allows the following use cases:
- Private sites for personal use, for example:
- Apps for You personally (like a personal diary)
- Apps for You as a freelancer (like a bookkeeping, invoicing or project management app)
- Apps for Your family (like a private photo gallery)
- Experimental local Kirby setups for Your personal use (for example to try out Kirby features)
However, the following use cases are _not_ covered and need a **[paid license](#usage-for-a-public-site)**:
- Intranets for companies, authorities or organizations, no matter if on a local or public server
- (Internal) apps for teams or entire companies, authorities or organizations
- Websites that are accessible by the public, even for personal/non-commercial purposes
- Use of Kirby as a local CMS for a static or headless site without a license for the frontend domain(s)
### Usage for an Extension Demo
Extension Demos are not real Websites. We want to encourage you to build and showcase your themes and plugins.
Therefore, You may **operate Extension Demos without purchasing a license**.
Please note that this does _not_ apply to store fronts or other types of sites used to promote free or commercial themes or plugins. If such a site is built with Kirby as well, it is a Public Site and needs a **[paid license](#usage-for-a-public-site)**.
## Restrictions
### Legal Restrictions
You may only use Kirby in a manner that complies with any and all **applicable laws** in the jurisdictions in which You use Kirby. Please respect all applicable restrictions concerning **privacy and intellectual property rights**.
### Making Copies
You may make **copies of Kirby** in any machine readable form solely for purposes of **deploying a Website to a server, developing a Website on a personal computer or server or as a backup**, provided that You reproduce Kirby in its original form and with all proprietary notices on the copy.
You may _not_ reproduce Kirby or its Source Code, in whole or in part, for **any other purpose**.
### Modification of the Source Code
You may **alter, modify or extend the Source Code** for Your own use. You may also **commission a third party** to perform those modifications for You.
However You may _not_:
- **alter or circumvent the licensing features**, including (but not limited to) the license validation and payment prompts or
- **resell, redistribute or transfer** the modified or derivative version.
Please note that We **can't provide technical support** for modified or derivative versions of the Source Code.
### Your Relationship to Third Parties
You are generally _not_ allowed to **sell, assign, license, disclose, distribute, or otherwise transfer or make available** Kirby or its Source Code, in whole or in part, in any form to any third parties.
The following cases are exempted from this restriction:
- Kirby licenses may be transferred to a new licensee by requesting the transfer from Us ([see above](#usage-for-a-public-site)).
- You may create Websites for third parties (e.g. as an agency or freelancer for a client). Together with this Website, You may bill Your client for the used Kirby license. You may also include the license price in a flat rate. Please note that the licensee in both of these cases is still You unless You request to transfer the license to Your client. If Your price exceeds the price You paid to Us, You need to give Your client the option to purchase the license directly from Us.
- You may make Kirby available to customers via a Software-as-a-Service (SaaS) offering, provided You ensure that each Website has a valid Kirby license purchased either by You or Your customer. If multiple customers share a Website, each customer needs at least one license. Your offering _must not_ appear to be provided or officially endorsed by Us.
- You may make a Kirby installation available to employees or partners of You or Your Website client. You may also disclose and distribute Kirbys Source Code to Your client together with the source code of the Website You created for them.
- You may disclose the Source Code to individuals or companies that are involved in the development or operation of Your Website (e.g. agencies, design or development freelancers, hosting providers or administrators).
E.g. the following cases are explicitly **_not_ allowed**:
- Selling, licensing or distributing a new product based on Kirby that modifies or hides Kirbys identity as a Content Management System (CMS)
- Forking Kirby and selling the modified version ([see above](#restrictions__modification-of-the-source-code))
- Buying licenses in bulk and reselling them in your own shop
- Bundling or including Kirbys Source Code in the publication and/or distribution of a Websites source code or a (free or paid) theme or plugin (please use Git submodules or Composer or provide a link to Our repository or website instead)
### Disallowed Uses
The following uses of Kirby are _not_ covered by this Agreement and will result in the termination of the license:
- Direct or indirect use of Kirby in **critical infrastructure** (e.g. water and energy services, public health, financial services, public security services) or **high-risk environments** (e.g. handling of harmful or dangerous materials). The use in Websites without connection to core processes is allowed.
- Use of Kirby for Websites that contain **misinformation, hate speech or discriminating content** based on age, gender, gender identity, race, sexuality, religion, nationality, serious illnesses or disabilities, no matter who authored this content. Misinformation is defined as content that is false or misleading and may lead to significant risk of physical or societal harm.
- Use of Kirby by **companies or individuals who**:
- lobby for, promote, derive a majority of income from or are significantly invested in:
- the production of tobacco or weapons,
- any prison or jail operated for profit,
- any action or facility that supports or contributes to:
- gambling, adversely addictive behaviours or
- deforestation.
- lobby against, or derive a majority of income from actions that discourage or frustrate:
- peace,
- access to the rights set out in the Universal Declaration of Human Rights and the Convention on the Rights of the Child,
- peaceful assembly and association (including worker associations),
- a safe environment or action to curtail the use of fossil fuels or to prevent climate change or
- democratic processes.
### Other Restrictions
You may also _not_:
- **extract parts of the Source Code** for use in other programs or projects (unless the code file in question is explicitly licensed under the terms of the MIT license) or
- **remove or alter any proprietary notices** on Kirby.
## Technical Support
Technical support is **provided as described on Our website** at <https://getkirby.com>. **No representations or guarantees** are made regarding the response time in which support questions are answered, however We will do Our best to respond quickly.
We reserve the right to **limit technical support for free licenses**.
## Refund Policy
We offer a **14-day**, money back refund policy if Kirby didn't work out for Your project.
If you need a refund, please get in touch directly at <support@getkirby.com>.
## No Warranty
KIRBY IS OFFERED ON AN **"AS-IS" BASIS** AND **NO WARRANTY**, EITHER EXPRESSED OR IMPLIED, IS GIVEN. WE EXPRESSLY DISCLAIM ALL WARRANTIES OF ANY KIND, WHETHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. YOU ASSUME ALL RISK ASSOCIATED WITH THE QUALITY, PERFORMANCE, INSTALLATION AND USE OF KIRBY INCLUDING, BUT NOT LIMITED TO, THE RISKS OF PROGRAM ERRORS, DAMAGE TO EQUIPMENT, LOSS OF DATA OR SOFTWARE PROGRAMS, OR UNAVAILABILITY OR INTERRUPTION OF OPERATIONS. **YOU ARE SOLELY RESPONSIBLE** FOR DETERMINING THE APPROPRIATENESS OF USE OF KIRBY AND ASSUME ALL RISKS ASSOCIATED WITH ITS USE. THIS PARAGRAPH ALSO APPLIES TO YOU IF YOU ARE NOT THE LICENSEE (E.G. IF YOU USE KIRBY WHILE SOMEONE ELSE IS THE LICENSEE).
## Term, Termination and Modification
You may use Kirby under this Agreement until either party terminates this Agreement as described in this paragraph. Either party may **terminate the Agreement** at any time, upon notice to the other party in textual form (via email or letter). Upon termination, all or the specified **licenses granted to You will terminate**, and You will **immediately uninstall and cease all use** of Kirby. If not all licenses are terminated, You may continue to use Kirby for the Websites with active licenses. The sections entitled "No Warranty", "Indemnification" and "Limitation of Liability" will **survive any termination** of this Agreement.
We may **modify Kirby and this Agreement** with notice to You either via email or by publishing content on the Kirby website at https://getkirby.com, including but not limited to changing the functionality or appearance of Kirby. Any such modification will **become binding on You** unless You terminate this Agreement. Changes to this Agreement that constrain Your rights to a great extent will only become effective with Your approval in textual or electronic form.
## Indemnification
By accepting the Agreement, you **agree to indemnify and otherwise hold harmless** Us as well as Our officers, employees, agents, subsidiaries, affiliates and other partners from any direct, indirect, incidental, special, consequential or exemplary damages arising out of, relating to, or resulting from your use of Kirby or any other matter relating to Kirby. This paragraph also applies to you if you are not the licensee (e.g. if you use Kirby while someone else is the licensee).
## Limitation of Liability
YOU EXPRESSLY UNDERSTAND AND AGREE THAT **WE SHALL NOT BE LIABLE** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR EXEMPLARY DAMAGES, INCLUDING BUT NOT LIMITED TO, DAMAGES FOR LOSS OF PROFITS, GOODWILL, USE, DATA OR OTHER INTANGIBLE LOSSES (EVEN IF WE HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES). SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF THE LIMITATION OR EXCLUSION OF LIABILITY FOR INCIDENTAL OR CONSEQUENTIAL DAMAGES. ACCORDINGLY, **SOME OF THE ABOVE LIMITATIONS MAY NOT APPLY TO YOU**. **IN NO EVENT WILL OUR TOTAL CUMULATIVE DAMAGES EXCEED** THE FEES YOU PAID TO US UNDER THIS AGREEMENT IN THE MOST RECENT TWELVE-MONTH PERIOD. THIS PARAGRAPH ALSO APPLIES TO YOU IF YOU ARE NOT THE LICENSEE (E.G. IF YOU USE KIRBY WHILE SOMEONE ELSE IS THE LICENSEE).
## All Rights Reserved
Bastian Allgeier **owns all rights**, title and interest to Kirby (including all intellectual property rights) and **reserves all rights to Kirby** that are not expressly granted in this Agreement.
## Applicable Law & Place of Jurisdiction
1. For all disputes arising out of or in connection with this Agreement, the courts competent for Neckargemünd, Germany, shall have exclusive jurisdiction. However, We shall have the choice to file lawsuits against You before the courts competent for Your place of business.
2. If You reside in Germany, para. 1 shall only apply if You are a merchant, a legal entity under public law or a special fund under public law.
3. If You don't reside in Germany, but in a different member state of the European Union, para. 1 shall only apply if You are not a consumer under Art. 17 of the regulation (EU) No. 1215/2012. In that case, You shall be entitled to file actions against Us either at Our place of business or at the courts competent at the place where You usually reside. We, on the other hand, are only entitled to bring proceedings against You in the courts of the Member State in which You are domiciled.
4. If You neither reside in Germany nor in a member state of the EU, the applicability of para. 1 remains unaffected.
## Severability Clause
Should any provision of this Agreement be or become invalid, void or unenforceable, in whole or in part, at present or in the future, this shall not affect the validity of the remaining provisions of this Agreement. The same shall apply if a gap requiring supplementation arises after conclusion of this Agreement. The parties shall replace the invalid, void or unenforceable provision or gap requiring filling by a valid provision which in its legal or economic content takes account of the invalid, void provision and the overall content of the agreement. § Section 139 of the German Civil Code (partial invalidity) is expressly waived.
## Questions?
Due to Kirby's flexibility, you may have special use cases or requirements that don't fit this Agreement.
If that's the case or if you have any questions, feel free to get in touch: <support@getkirby.com>. We are happy to think outside the box and find custom license solutions for your creative application of Kirby.

49
kirby/README.md Normal file
View file

@ -0,0 +1,49 @@
[<img src="http://getkirby.com/assets/images/github/kirby.jpg" width="300">](https://getkirby.com)
[![Release](https://badgen.net/github/release/getkirby/kirby/stable?color=yellow)](https://github.com/getkirby/kirby/releases/latest)
[![CI Status](https://github.com/getkirby/kirby/workflows/CI/badge.svg)](https://github.com/getkirby/kirby/actions?query=workflow%3ACI)
[![Coverage Status](https://badgen.net/codecov/c/gh/getkirby/kirby/main?label=coverage)](https://codecov.io/gh/getkirby/kirby)
[![Downloads](https://badgen.net/packagist/dt/getkirby/cms?color=red)](https://github.com/getkirby/kirby/releases/latest)
[![Twitter](https://badgen.net/twitter/follow/getkirby?color=cyan)](https://twitter.com/getkirby)
**Kirby: the CMS that adapts to any project, loved by developers and editors alike.**
With Kirby, you build your own ideal interface. Combine forms, galleries, articles, spreadsheets and more into an amazing editing experience. You can learn more about Kirby at [getkirby.com](https://getkirby.com).
This is Kirby's core application folder. Get started with one of the following repositories instead:
- [Starterkit](https://github.com/getkirby/starterkit)
- [Plainkit](https://github.com/getkirby/plainkit)
<img src="http://getkirby.com/assets/images/github/kirby-screen.png" />
### Try Kirby for free
Kirby is not free software. However, you can try Kirby and the Starterkit on your local machine or on a test server as long as you need to make sure it is the right tool for your next project. … and when youre convinced, [buy your license](https://getkirby.com/buy).
### Contribute
**Found a bug?**
Please post all bug reports in our [issue tracker](https://github.com/getkirby/kirby/issues).
**Suggest a feature**
If you have ideas for a feature or enhancement for Kirby, please use our [feedback platform](https://feedback.getkirby.com).
**Translations, bug fixes, code contributions ...**
Read about how to contribute to the development in our [contributing guide](/.github/CONTRIBUTING.md).
## What's Kirby?
- **[getkirby.com](https://getkirby.com)** Get to know the CMS.
- **[Try it](https://getkirby.com/try)** Take a test ride with our online demo. Or download one of our kits to get started.
- **[Documentation](https://getkirby.com/docs/guide)** Read the official guide, reference and cookbook recipes.
- **[Issues](https://github.com/getkirby/kirby/issues)** Report bugs and other problems.
- **[Feedback](https://feedback.getkirby.com)** You have an idea for Kirby? Share it.
- **[Forum](https://forum.getkirby.com)** Whenever you get stuck, don't hesitate to reach out for questions and support.
- **[Discord](https://chat.getkirby.com)** Hang out and meet the community.
- **[Twitter](https://twitter.com/getkirby)** Spread the word.
- **[Instagram](https://www.instagram.com/getkirby/)** Share your creations: #madewithkirby.
---
© 2009-2022 Bastian Allgeier
[getkirby.com](https://getkirby.com) · [License agreement](https://getkirby.com/license)

3
kirby/SECURITY.md Normal file
View file

@ -0,0 +1,3 @@
# Security Policy
Please see the [Security Policy on the Kirby website](https://getkirby.com/security) for a list of the currently supported Kirby versions and of past security incidents as well as for information on how to report security vulnerabilities in the Kirby core or in the Panel.

83
kirby/assets/whoops.css Normal file
View file

@ -0,0 +1,83 @@
body {
background: #efefef;
font: normal normal 400 12px/1.5 -apple-system, BlinkMacSystemFont, Segoe UI,
Roboto, Helvetica, Arial, sans-serif;
}
.left-panel {
background: transparent;
}
header {
background-color: #313740;
}
.exc-title-primary {
color: hsl(0, 71%, 55%);
}
.frame.active {
color: hsl(0, 71%, 55%);
box-shadow: inset -5px 0 0 0 #d16464;
}
.frame:not(.active):hover {
background: rgba(203, 215, 229, 0.5);
}
.rightButton {
color: #999;
box-shadow: inset 0 0 0 1px #777;
border-radius: 0;
}
.rightButton:hover {
box-shadow: inset 0 0 0 1px #555;
color: #777;
}
.details-heading {
color: #7e9abf;
font-weight: 500;
}
.frame-code {
background: #000;
}
pre.code-block,
code.code-block,
.frame-args.code-block,
.frame-args.code-block samp {
background: #16171a;
}
.linenums li.current {
background: transparent;
}
.linenums li.current.active {
background: rgba(209, 100, 100, 0.3);
}
pre .atv,
code .atv,
pre .str,
code .str {
color: #a7bd68;
}
pre .tag,
code .tag {
color: #d16464;
}
pre .kwd,
code .kwd {
color: #8abeb7;
}
pre .atn,
code .atn {
color: #de935f;
}

35
kirby/bootstrap.php Normal file
View file

@ -0,0 +1,35 @@
<?php
/**
* Validate the PHP version to already
* stop at older or too recent versions
*/
if (
version_compare(PHP_VERSION, '7.4.0', '>=') === false ||
version_compare(PHP_VERSION, '8.2.0', '<') === false
) {
die(include __DIR__ . '/views/php.php');
}
if (is_file($autoloader = dirname(__DIR__) . '/vendor/autoload.php')) {
/**
* Always prefer a site-wide Composer autoloader
* if it exists, it means that the user has probably
* installed additional packages
*/
include $autoloader;
} elseif (is_file($autoloader = __DIR__ . '/vendor/autoload.php')) {
/**
* Fall back to the local autoloader if that exists
*/
include $autoloader;
} else {
/**
* If neither one exists, don't bother searching;
* it's a custom directory setup and the users need to
* load the autoloader themselves
*/
}

3281
kirby/cacert.pem Normal file

File diff suppressed because it is too large Load diff

90
kirby/composer.json Normal file
View file

@ -0,0 +1,90 @@
{
"name": "getkirby/cms",
"description": "The Kirby 3 core",
"license": "proprietary",
"type": "kirby-cms",
"version": "3.6.3",
"keywords": [
"kirby",
"cms",
"core"
],
"authors": [
{
"name": "Kirby Team",
"email": "support@getkirby.com",
"homepage": "https://getkirby.com"
}
],
"homepage": "https://getkirby.com",
"support": {
"email": "support@getkirby.com",
"issues": "https://github.com/getkirby/kirby/issues",
"forum": "https://forum.getkirby.com",
"source": "https://github.com/getkirby/kirby"
},
"_comment": "TODO: psr/log is not used by Kirby; drop pinned version when Kirby no longer supports PHP 7",
"require": {
"php": ">=7.4.0 <8.2.0",
"ext-ctype": "*",
"ext-mbstring": "*",
"claviska/simpleimage": "3.6.5",
"filp/whoops": "2.14.5",
"getkirby/composer-installer": "^1.2.1",
"laminas/laminas-escaper": "2.9.0",
"michelf/php-smartypants": "1.8.1",
"phpmailer/phpmailer": "6.5.4",
"psr/log": "1.1.4",
"symfony/polyfill-intl-idn": "1.24.0",
"symfony/polyfill-mbstring": "1.24.0"
},
"replace": {
"symfony/polyfill-php72": "*"
},
"autoload": {
"psr-4": {
"Kirby\\": "src/"
},
"classmap": [
"dependencies/"
],
"files": [
"config/setup.php",
"config/helpers.php"
]
},
"config": {
"allow-plugins": {
"getkirby/composer-installer": true
},
"optimize-autoloader": true,
"platform-check": false
},
"extra": {
"unused": [
"symfony/polyfill-intl-idn"
]
},
"scripts": {
"post-update-cmd": "curl -o cacert.pem https://curl.se/ca/cacert.pem",
"analyze": [
"@analyze:composer",
"@analyze:psalm",
"@analyze:phpcpd",
"@analyze:phpmd"
],
"analyze:composer": "composer validate --strict --no-check-version --no-check-all",
"analyze:phpcpd": "phpcpd --fuzzy --exclude tests --exclude vendor .",
"analyze:phpmd": "phpmd . ansi phpmd.xml.dist --exclude 'dependencies/*,tests/*,vendor/*'",
"analyze:psalm": "psalm",
"build": "./scripts/build",
"ci": [
"@fix",
"@analyze",
"@test"
],
"fix": "php-cs-fixer fix",
"test": "phpunit --stderr --coverage-html=tests/coverage",
"zip": "composer archive --format=zip --file=dist"
}
}

746
kirby/composer.lock generated Normal file
View file

@ -0,0 +1,746 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "d4cf75084dae428fe0ab54124637d51f",
"packages": [
{
"name": "claviska/simpleimage",
"version": "3.6.5",
"source": {
"type": "git",
"url": "https://github.com/claviska/SimpleImage.git",
"reference": "00f90662686696b9b7157dbb176183aabe89700f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/claviska/SimpleImage/zipball/00f90662686696b9b7157dbb176183aabe89700f",
"reference": "00f90662686696b9b7157dbb176183aabe89700f",
"shasum": ""
},
"require": {
"ext-gd": "*",
"league/color-extractor": "0.3.*",
"php": ">=5.6.0"
},
"type": "library",
"autoload": {
"psr-0": {
"claviska": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Cory LaViska",
"homepage": "http://www.abeautifulsite.net/",
"role": "Developer"
}
],
"description": "A PHP class that makes working with images as simple as possible.",
"support": {
"issues": "https://github.com/claviska/SimpleImage/issues",
"source": "https://github.com/claviska/SimpleImage/tree/3.6.5"
},
"funding": [
{
"url": "https://github.com/claviska",
"type": "github"
}
],
"time": "2021-12-01T12:42:55+00:00"
},
{
"name": "filp/whoops",
"version": "2.14.5",
"source": {
"type": "git",
"url": "https://github.com/filp/whoops.git",
"reference": "a63e5e8f26ebbebf8ed3c5c691637325512eb0dc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/filp/whoops/zipball/a63e5e8f26ebbebf8ed3c5c691637325512eb0dc",
"reference": "a63e5e8f26ebbebf8ed3c5c691637325512eb0dc",
"shasum": ""
},
"require": {
"php": "^5.5.9 || ^7.0 || ^8.0",
"psr/log": "^1.0.1 || ^2.0 || ^3.0"
},
"require-dev": {
"mockery/mockery": "^0.9 || ^1.0",
"phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3",
"symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0"
},
"suggest": {
"symfony/var-dumper": "Pretty print complex values better with var-dumper available",
"whoops/soap": "Formats errors as SOAP responses"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.7-dev"
}
},
"autoload": {
"psr-4": {
"Whoops\\": "src/Whoops/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Filipe Dobreira",
"homepage": "https://github.com/filp",
"role": "Developer"
}
],
"description": "php error handling for cool kids",
"homepage": "https://filp.github.io/whoops/",
"keywords": [
"error",
"exception",
"handling",
"library",
"throwable",
"whoops"
],
"support": {
"issues": "https://github.com/filp/whoops/issues",
"source": "https://github.com/filp/whoops/tree/2.14.5"
},
"funding": [
{
"url": "https://github.com/denis-sokolov",
"type": "github"
}
],
"time": "2022-01-07T12:00:00+00:00"
},
{
"name": "getkirby/composer-installer",
"version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/getkirby/composer-installer.git",
"reference": "c98ece30bfba45be7ce457e1102d1b169d922f3d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/getkirby/composer-installer/zipball/c98ece30bfba45be7ce457e1102d1b169d922f3d",
"reference": "c98ece30bfba45be7ce457e1102d1b169d922f3d",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0 || ^2.0"
},
"require-dev": {
"composer/composer": "^1.8 || ^2.0"
},
"type": "composer-plugin",
"extra": {
"class": "Kirby\\ComposerInstaller\\Plugin"
},
"autoload": {
"psr-4": {
"Kirby\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Kirby's custom Composer installer for the Kirby CMS and for Kirby plugins",
"homepage": "https://getkirby.com",
"support": {
"issues": "https://github.com/getkirby/composer-installer/issues",
"source": "https://github.com/getkirby/composer-installer/tree/1.2.1"
},
"funding": [
{
"url": "https://getkirby.com/buy",
"type": "custom"
}
],
"time": "2020-12-28T12:54:39+00:00"
},
{
"name": "laminas/laminas-escaper",
"version": "2.9.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-escaper.git",
"reference": "891ad70986729e20ed2e86355fcf93c9dc238a5f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/891ad70986729e20ed2e86355fcf93c9dc238a5f",
"reference": "891ad70986729e20ed2e86355fcf93c9dc238a5f",
"shasum": ""
},
"require": {
"php": "^7.3 || ~8.0.0 || ~8.1.0"
},
"conflict": {
"zendframework/zend-escaper": "*"
},
"require-dev": {
"laminas/laminas-coding-standard": "~2.3.0",
"phpunit/phpunit": "^9.3",
"psalm/plugin-phpunit": "^0.12.2",
"vimeo/psalm": "^3.16"
},
"suggest": {
"ext-iconv": "*",
"ext-mbstring": "*"
},
"type": "library",
"autoload": {
"psr-4": {
"Laminas\\Escaper\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"description": "Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs",
"homepage": "https://laminas.dev",
"keywords": [
"escaper",
"laminas"
],
"support": {
"chat": "https://laminas.dev/chat",
"docs": "https://docs.laminas.dev/laminas-escaper/",
"forum": "https://discourse.laminas.dev",
"issues": "https://github.com/laminas/laminas-escaper/issues",
"rss": "https://github.com/laminas/laminas-escaper/releases.atom",
"source": "https://github.com/laminas/laminas-escaper"
},
"funding": [
{
"url": "https://funding.communitybridge.org/projects/laminas-project",
"type": "community_bridge"
}
],
"time": "2021-09-02T17:10:53+00:00"
},
{
"name": "league/color-extractor",
"version": "0.3.2",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/color-extractor.git",
"reference": "837086ec60f50c84c611c613963e4ad2e2aec806"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/color-extractor/zipball/837086ec60f50c84c611c613963e4ad2e2aec806",
"reference": "837086ec60f50c84c611c613963e4ad2e2aec806",
"shasum": ""
},
"require": {
"ext-gd": "*",
"php": ">=5.4.0"
},
"replace": {
"matthecat/colorextractor": "*"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "~2",
"phpunit/phpunit": "~5"
},
"type": "library",
"autoload": {
"psr-4": {
"": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mathieu Lechat",
"email": "math.lechat@gmail.com",
"homepage": "http://matthecat.com",
"role": "Developer"
}
],
"description": "Extract colors from an image as a human would do.",
"homepage": "https://github.com/thephpleague/color-extractor",
"keywords": [
"color",
"extract",
"human",
"image",
"palette"
],
"support": {
"issues": "https://github.com/thephpleague/color-extractor/issues",
"source": "https://github.com/thephpleague/color-extractor/tree/master"
},
"time": "2016-12-15T09:30:02+00:00"
},
{
"name": "michelf/php-smartypants",
"version": "1.8.1",
"source": {
"type": "git",
"url": "https://github.com/michelf/php-smartypants.git",
"reference": "47d17c90a4dfd0ccf1f87e25c65e6c8012415aad"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/michelf/php-smartypants/zipball/47d17c90a4dfd0ccf1f87e25c65e6c8012415aad",
"reference": "47d17c90a4dfd0ccf1f87e25c65e6c8012415aad",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"autoload": {
"psr-0": {
"Michelf": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Michel Fortin",
"email": "michel.fortin@michelf.ca",
"homepage": "https://michelf.ca/",
"role": "Developer"
},
{
"name": "John Gruber",
"homepage": "https://daringfireball.net/"
}
],
"description": "PHP SmartyPants",
"homepage": "https://michelf.ca/projects/php-smartypants/",
"keywords": [
"dashes",
"quotes",
"spaces",
"typographer",
"typography"
],
"support": {
"issues": "https://github.com/michelf/php-smartypants/issues",
"source": "https://github.com/michelf/php-smartypants/tree/1.8.1"
},
"time": "2016-12-13T01:01:17+00:00"
},
{
"name": "phpmailer/phpmailer",
"version": "v6.5.4",
"source": {
"type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "c0d9f7dd3c2aa247ca44791e9209233829d82285"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/c0d9f7dd3c2aa247ca44791e9209233829d82285",
"reference": "c0d9f7dd3c2aa247ca44791e9209233829d82285",
"shasum": ""
},
"require": {
"ext-ctype": "*",
"ext-filter": "*",
"ext-hash": "*",
"php": ">=5.5.0"
},
"require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
"doctrine/annotations": "^1.2",
"php-parallel-lint/php-console-highlighter": "^0.5.0",
"php-parallel-lint/php-parallel-lint": "^1.3.1",
"phpcompatibility/php-compatibility": "^9.3.5",
"roave/security-advisories": "dev-latest",
"squizlabs/php_codesniffer": "^3.6.2",
"yoast/phpunit-polyfills": "^1.0.0"
},
"suggest": {
"ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses",
"hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication",
"league/oauth2-google": "Needed for Google XOAUTH2 authentication",
"psr/log": "For optional PSR-3 debug logging",
"stevenmaguire/oauth2-microsoft": "Needed for Microsoft XOAUTH2 authentication",
"symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)"
},
"type": "library",
"autoload": {
"psr-4": {
"PHPMailer\\PHPMailer\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1-only"
],
"authors": [
{
"name": "Marcus Bointon",
"email": "phpmailer@synchromedia.co.uk"
},
{
"name": "Jim Jagielski",
"email": "jimjag@gmail.com"
},
{
"name": "Andy Prevost",
"email": "codeworxtech@users.sourceforge.net"
},
{
"name": "Brent R. Matzelle"
}
],
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"support": {
"issues": "https://github.com/PHPMailer/PHPMailer/issues",
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.5.4"
},
"funding": [
{
"url": "https://github.com/Synchro",
"type": "github"
}
],
"time": "2022-02-17T08:19:04+00:00"
},
{
"name": "psr/log",
"version": "1.1.4",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "d49695b909c3b7628b6289db5479a1c204601f11"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
"reference": "d49695b909c3b7628b6289db5479a1c204601f11",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Log\\": "Psr/Log/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"homepage": "https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
],
"support": {
"source": "https://github.com/php-fig/log/tree/1.1.4"
},
"time": "2021-05-03T11:20:27+00:00"
},
{
"name": "symfony/polyfill-intl-idn",
"version": "v1.24.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "749045c69efb97c70d25d7463abba812e91f3a44"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/749045c69efb97c70d25d7463abba812e91f3a44",
"reference": "749045c69efb97c70d25d7463abba812e91f3a44",
"shasum": ""
},
"require": {
"php": ">=7.1",
"symfony/polyfill-intl-normalizer": "^1.10",
"symfony/polyfill-php72": "^1.10"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Intl\\Idn\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Laurent Bassin",
"email": "laurent@bassin.info"
},
{
"name": "Trevor Rowbotham",
"email": "trevor.rowbotham@pm.me"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"idn",
"intl",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.24.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2021-09-14T14:02:44+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
"version": "v1.25.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8",
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Intl\\Normalizer\\": ""
},
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for intl's Normalizer class and related functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"intl",
"normalizer",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.25.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2021-02-19T12:13:01+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.24.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825",
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"provide": {
"ext-mbstring": "*"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.24.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2021-11-30T18:21:41+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=7.4.0 <8.2.0",
"ext-ctype": "*",
"ext-mbstring": "*"
},
"platform-dev": [],
"plugin-api-version": "2.1.0"
}

80
kirby/config/aliases.php Normal file
View file

@ -0,0 +1,80 @@
<?php
return [
// cms classes
'collection' => 'Kirby\Cms\Collection',
'field' => 'Kirby\Cms\Field',
'file' => 'Kirby\Cms\File',
'files' => 'Kirby\Cms\Files',
'find' => 'Kirby\Cms\Find',
'html' => 'Kirby\Cms\Html',
'kirby' => 'Kirby\Cms\App',
'page' => 'Kirby\Cms\Page',
'pages' => 'Kirby\Cms\Pages',
'pagination' => 'Kirby\Cms\Pagination',
'r' => 'Kirby\Cms\R',
'response' => 'Kirby\Cms\Response',
's' => 'Kirby\Cms\S',
'sane' => 'Kirby\Sane\Sane',
'site' => 'Kirby\Cms\Site',
'structure' => 'Kirby\Cms\Structure',
'url' => 'Kirby\Cms\Url',
'user' => 'Kirby\Cms\User',
'users' => 'Kirby\Cms\Users',
'visitor' => 'Kirby\Cms\Visitor',
// data handler
'data' => 'Kirby\Data\Data',
'json' => 'Kirby\Data\Json',
'yaml' => 'Kirby\Data\Yaml',
// file classes
'asset' => 'Kirby\Filesystem\Asset',
'dir' => 'Kirby\Filesystem\Dir',
'f' => 'Kirby\Filesystem\F',
'mime' => 'Kirby\Filesystem\Mime',
// data classes
'database' => 'Kirby\Database\Database',
'db' => 'Kirby\Database\Db',
// exceptions
'errorpageexception' => 'Kirby\Exception\ErrorPageException',
// http classes
'cookie' => 'Kirby\Http\Cookie',
'header' => 'Kirby\Http\Header',
'remote' => 'Kirby\Http\Remote',
'server' => 'Kirby\Http\Server',
// image classes
'dimensions' => 'Kirby\Image\Dimensions',
// panel classes
'panel' => 'Kirby\Panel\Panel',
// toolkit classes
'a' => 'Kirby\Toolkit\A',
'c' => 'Kirby\Toolkit\Config',
'config' => 'Kirby\Toolkit\Config',
'escape' => 'Kirby\Toolkit\Escape',
'i18n' => 'Kirby\Toolkit\I18n',
'obj' => 'Kirby\Toolkit\Obj',
'str' => 'Kirby\Toolkit\Str',
'tpl' => 'Kirby\Toolkit\Tpl',
'v' => 'Kirby\Toolkit\V',
'xml' => 'Kirby\Toolkit\Xml',
// TODO: remove in 4.0.0
'kirby\cms\asset' => 'Kirby\Filesystem\Asset',
'kirby\cms\dir' => 'Kirby\Filesystem\Dir',
'kirby\cms\filename' => 'Kirby\Filesystem\Filename',
'kirby\cms\filefoundation' => 'Kirby\Filesystem\IsFile',
'kirby\cms\form' => 'Kirby\Form\Form',
'kirby\cms\kirbytag' => 'Kirby\Text\KirbyTag',
'kirby\cms\kirbytags' => 'Kirby\Text\KirbyTags',
'kirby\toolkit\dir' => 'Kirby\Filesystem\Dir',
'kirby\toolkit\f' => 'Kirby\Filesystem\F',
'kirby\toolkit\file' => 'Kirby\Filesystem\File',
'kirby\toolkit\mime' => 'Kirby\Filesystem\Mime',
];

View file

@ -0,0 +1,27 @@
<?php
use Kirby\Exception\PermissionException;
return function () {
$auth = $this->kirby()->auth();
$allowImpersonation = $this->kirby()->option('api.allowImpersonation') ?? false;
// csrf token check
if (
$auth->type($allowImpersonation) === 'session' &&
$auth->csrf() === false
) {
throw new PermissionException('Unauthenticated');
}
// get user from session or basic auth
if ($user = $auth->user(null, $allowImpersonation)) {
if ($user->role()->permissions()->for('access', 'panel') === false) {
throw new PermissionException(['key' => 'access.panel']);
}
return $user;
}
throw new PermissionException('Unauthenticated');
};

View file

@ -0,0 +1,70 @@
<?php
/**
* Api Collection Definitions
*/
return [
/**
* Children
*/
'children' => [
'model' => 'page',
'type' => 'Kirby\Cms\Pages',
'view' => 'compact'
],
/**
* Files
*/
'files' => [
'model' => 'file',
'type' => 'Kirby\Cms\Files'
],
/**
* Languages
*/
'languages' => [
'model' => 'language',
'type' => 'Kirby\Cms\Languages'
],
/**
* Pages
*/
'pages' => [
'model' => 'page',
'type' => 'Kirby\Cms\Pages',
'view' => 'compact'
],
/**
* Roles
*/
'roles' => [
'model' => 'role',
'type' => 'Kirby\Cms\Roles',
'view' => 'compact'
],
/**
* Translations
*/
'translations' => [
'model' => 'translation',
'type' => 'Kirby\Cms\Translations',
'view' => 'compact'
],
/**
* Users
*/
'users' => [
'default' => fn () => $this->users(),
'model' => 'user',
'type' => 'Kirby\Cms\Users',
'view' => 'compact'
]
];

View file

@ -0,0 +1,20 @@
<?php
/**
* Api Model Definitions
*/
return [
'File' => include __DIR__ . '/models/File.php',
'FileBlueprint' => include __DIR__ . '/models/FileBlueprint.php',
'FileVersion' => include __DIR__ . '/models/FileVersion.php',
'Language' => include __DIR__ . '/models/Language.php',
'Page' => include __DIR__ . '/models/Page.php',
'PageBlueprint' => include __DIR__ . '/models/PageBlueprint.php',
'Role' => include __DIR__ . '/models/Role.php',
'Site' => include __DIR__ . '/models/Site.php',
'SiteBlueprint' => include __DIR__ . '/models/SiteBlueprint.php',
'System' => include __DIR__ . '/models/System.php',
'Translation' => include __DIR__ . '/models/Translation.php',
'User' => include __DIR__ . '/models/User.php',
'UserBlueprint' => include __DIR__ . '/models/UserBlueprint.php',
];

View file

@ -0,0 +1,122 @@
<?php
use Kirby\Cms\File;
use Kirby\Form\Form;
/**
* File
*/
return [
'fields' => [
'blueprint' => fn (File $file) => $file->blueprint(),
'content' => fn (File $file) => Form::for($file)->values(),
'dimensions' => fn (File $file) => $file->dimensions()->toArray(),
'dragText' => fn (File $file) => $file->panel()->dragText(),
'exists' => fn (File $file) => $file->exists(),
'extension' => fn (File $file) => $file->extension(),
'filename' => fn (File $file) => $file->filename(),
'id' => fn (File $file) => $file->id(),
'link' => fn (File $file) => $file->panel()->url(true),
'mime' => fn (File $file) => $file->mime(),
'modified' => fn (File $file) => $file->modified('c'),
'name' => fn (File $file) => $file->name(),
'next' => fn (File $file) => $file->next(),
'nextWithTemplate' => function (File $file) {
$files = $file->templateSiblings()->sorted();
$index = $files->indexOf($file);
return $files->nth($index + 1);
},
'niceSize' => fn (File $file) => $file->niceSize(),
'options' => fn (File $file) => $file->panel()->options(),
'panelIcon' => function (File $file) {
// TODO: remove in 3.7.0
// @codeCoverageIgnoreStart
deprecated('The API field file.panelIcon has been deprecated and will be removed in 3.7.0. Use file.panelImage instead');
return $file->panel()->image();
// @codeCoverageIgnoreEnd
},
'panelImage' => fn (File $file) => $file->panel()->image(),
'panelUrl' => fn (File $file) => $file->panel()->url(true),
'prev' => fn (File $file) => $file->prev(),
'prevWithTemplate' => function (File $file) {
$files = $file->templateSiblings()->sorted();
$index = $files->indexOf($file);
return $files->nth($index - 1);
},
'parent' => fn (File $file) => $file->parent(),
'parents' => fn (File $file) => $file->parents()->flip(),
'size' => fn (File $file) => $file->size(),
'template' => fn (File $file) => $file->template(),
'thumbs' => function ($file) {
if ($file->isResizable() === false) {
return null;
}
return [
'tiny' => $file->resize(128)->url(),
'small' => $file->resize(256)->url(),
'medium' => $file->resize(512)->url(),
'large' => $file->resize(768)->url(),
'huge' => $file->resize(1024)->url(),
];
},
'type' => fn (File $file) => $file->type(),
'url' => fn (File $file) => $file->url(),
],
'type' => 'Kirby\Cms\File',
'views' => [
'default' => [
'content',
'dimensions',
'exists',
'extension',
'filename',
'id',
'link',
'mime',
'modified',
'name',
'next' => 'compact',
'niceSize',
'parent' => 'compact',
'options',
'prev' => 'compact',
'size',
'template',
'type',
'url'
],
'compact' => [
'filename',
'id',
'link',
'type',
'url',
],
'panel' => [
'blueprint',
'content',
'dimensions',
'extension',
'filename',
'id',
'link',
'mime',
'modified',
'name',
'nextWithTemplate' => 'compact',
'niceSize',
'options',
'panelIcon',
'panelImage',
'parent' => 'compact',
'parents' => ['id', 'slug', 'title'],
'prevWithTemplate' => 'compact',
'template',
'type',
'url'
]
],
];

View file

@ -0,0 +1,18 @@
<?php
use Kirby\Cms\FileBlueprint;
/**
* FileBlueprint
*/
return [
'fields' => [
'name' => fn (FileBlueprint $blueprint) => $blueprint->name(),
'options' => fn (FileBlueprint $blueprint) => $blueprint->options(),
'tabs' => fn (FileBlueprint $blueprint) => $blueprint->tabs(),
'title' => fn (FileBlueprint $blueprint) => $blueprint->title(),
],
'type' => 'Kirby\Cms\FileBlueprint',
'views' => [
],
];

View file

@ -0,0 +1,59 @@
<?php
use Kirby\Cms\FileVersion;
/**
* FileVersion
*/
return [
'fields' => [
'dimensions' => fn (FileVersion $file) => $file->dimensions()->toArray(),
'exists' => fn (FileVersion $file) => $file->exists(),
'extension' => fn (FileVersion $file) => $file->extension(),
'filename' => fn (FileVersion $file) => $file->filename(),
'id' => fn (FileVersion $file) => $file->id(),
'mime' => fn (FileVersion $file) => $file->mime(),
'modified' => fn (FileVersion $file) => $file->modified('c'),
'name' => fn (FileVersion $file) => $file->name(),
'niceSize' => fn (FileVersion $file) => $file->niceSize(),
'size' => fn (FileVersion $file) => $file->size(),
'type' => fn (FileVersion $file) => $file->type(),
'url' => fn (FileVersion $file) => $file->url(),
],
'type' => 'Kirby\Cms\FileVersion',
'views' => [
'default' => [
'dimensions',
'exists',
'extension',
'filename',
'id',
'mime',
'modified',
'name',
'niceSize',
'size',
'type',
'url'
],
'compact' => [
'filename',
'id',
'type',
'url',
],
'panel' => [
'dimensions',
'extension',
'filename',
'id',
'mime',
'modified',
'name',
'niceSize',
'template',
'type',
'url'
]
],
];

View file

@ -0,0 +1,30 @@
<?php
use Kirby\Cms\Language;
/**
* Language
*/
return [
'fields' => [
'code' => fn (Language $language) => $language->code(),
'default' => fn (Language $language) => $language->isDefault(),
'direction' => fn (Language $language) => $language->direction(),
'locale' => fn (Language $language) => $language->locale(),
'name' => fn (Language $language) => $language->name(),
'rules' => fn (Language $language) => $language->rules(),
'url' => fn (Language $language) => $language->url(),
],
'type' => 'Kirby\Cms\Language',
'views' => [
'default' => [
'code',
'default',
'direction',
'locale',
'name',
'rules',
'url'
]
]
];

View file

@ -0,0 +1,128 @@
<?php
use Kirby\Cms\Page;
use Kirby\Form\Form;
/**
* Page
*/
return [
'fields' => [
'blueprint' => fn (Page $page) => $page->blueprint(),
'blueprints' => fn (Page $page) => $page->blueprints(),
'children' => fn (Page $page) => $page->children(),
'content' => fn (Page $page) => Form::for($page)->values(),
'drafts' => fn (Page $page) => $page->drafts(),
'errors' => fn (Page $page) => $page->errors(),
'files' => fn (Page $page) => $page->files()->sorted(),
'hasChildren' => fn (Page $page) => $page->hasChildren(),
'hasDrafts' => fn (Page $page) => $page->hasDrafts(),
'hasFiles' => fn (Page $page) => $page->hasFiles(),
'id' => fn (Page $page) => $page->id(),
'isSortable' => fn (Page $page) => $page->isSortable(),
/**
* @deprecated 3.6.0
* @todo Throw deprecated warning in 3.7.0
* @todo Remove in 3.8.0
* @codeCoverageIgnore
*/
'next' => function (Page $page) {
return $page
->nextAll()
->filter('intendedTemplate', $page->intendedTemplate())
->filter('status', $page->status())
->filter('isReadable', true)
->first();
},
'num' => fn (Page $page) => $page->num(),
'options' => fn (Page $page) => $page->panel()->options(['preview']),
/**
* @todo Remove in 3.7.0
* @codeCoverageIgnore
*/
'panelIcon' => function (Page $page) {
deprecated('The API field page.panelIcon has been deprecated and will be removed in 3.7.0. Use page.panelImage instead');
return $page->panel()->image();
},
'panelImage' => fn (Page $page) => $page->panel()->image(),
'parent' => fn (Page $page) => $page->parent(),
'parents' => fn (Page $page) => $page->parents()->flip(),
/**
* @deprecated 3.6.0
* @todo Throw deprecated warning in 3.7.0
* @todo Remove in 3.8.0
* @codeCoverageIgnore
*/
'prev' => function (Page $page) {
return $page
->prevAll()
->filter('intendedTemplate', $page->intendedTemplate())
->filter('status', $page->status())
->filter('isReadable', true)
->last();
},
'previewUrl' => fn (Page $page) => $page->previewUrl(),
'siblings' => function (Page $page) {
if ($page->isDraft() === true) {
return $page->parentModel()->children()->not($page);
} else {
return $page->siblings();
}
},
'slug' => fn (Page $page) => $page->slug(),
'status' => fn (Page $page) => $page->status(),
'template' => fn (Page $page) => $page->intendedTemplate()->name(),
'title' => fn (Page $page) => $page->title()->value(),
'url' => fn (Page $page) => $page->url(),
],
'type' => 'Kirby\Cms\Page',
'views' => [
'compact' => [
'id',
'title',
'url',
'num'
],
'default' => [
'content',
'id',
'status',
'num',
'options',
'parent' => 'compact',
'slug',
'template',
'title',
'url'
],
'panel' => [
'id',
'blueprint',
'content',
'status',
'options',
'next' => ['id', 'slug', 'title'],
'parents' => ['id', 'slug', 'title'],
'prev' => ['id', 'slug', 'title'],
'previewUrl',
'slug',
'title',
'url'
],
'selector' => [
'id',
'title',
'parent' => [
'id',
'title'
],
'children' => [
'hasChildren',
'id',
'panelIcon',
'panelImage',
'title',
],
]
],
];

View file

@ -0,0 +1,21 @@
<?php
use Kirby\Cms\PageBlueprint;
/**
* PageBlueprint
*/
return [
'fields' => [
'name' => fn (PageBlueprint $blueprint) => $blueprint->name(),
'num' => fn (PageBlueprint $blueprint) => $blueprint->num(),
'options' => fn (PageBlueprint $blueprint) => $blueprint->options(),
'preview' => fn (PageBlueprint $blueprint) => $blueprint->preview(),
'status' => fn (PageBlueprint $blueprint) => $blueprint->status(),
'tabs' => fn (PageBlueprint $blueprint) => $blueprint->tabs(),
'title' => fn (PageBlueprint $blueprint) => $blueprint->title(),
],
'type' => 'Kirby\Cms\PageBlueprint',
'views' => [
],
];

View file

@ -0,0 +1,23 @@
<?php
use Kirby\Cms\Role;
/**
* Role
*/
return [
'fields' => [
'description' => fn (Role $role) => $role->description(),
'name' => fn (Role $role) => $role->name(),
'permissions' => fn (Role $role) => $role->permissions()->toArray(),
'title' => fn (Role $role) => $role->title(),
],
'type' => 'Kirby\Cms\Role',
'views' => [
'compact' => [
'description',
'name',
'title'
]
]
];

View file

@ -0,0 +1,52 @@
<?php
use Kirby\Cms\Site;
use Kirby\Form\Form;
/**
* Site
*/
return [
'default' => fn () => $this->site(),
'fields' => [
'blueprint' => fn (Site $site) => $site->blueprint(),
'children' => fn (Site $site) => $site->children(),
'content' => fn (Site $site) => Form::for($site)->values(),
'drafts' => fn (Site $site) => $site->drafts(),
'files' => fn (Site $site) => $site->files()->sorted(),
'options' => fn (Site $site) => $site->permissions()->toArray(),
'previewUrl' => fn (Site $site) => $site->previewUrl(),
'title' => fn (Site $site) => $site->title()->value(),
'url' => fn (Site $site) => $site->url(),
],
'type' => 'Kirby\Cms\Site',
'views' => [
'compact' => [
'title',
'url'
],
'default' => [
'content',
'options',
'title',
'url'
],
'panel' => [
'title',
'blueprint',
'content',
'options',
'previewUrl',
'url'
],
'selector' => [
'title',
'children' => [
'id',
'title',
'panelIcon',
'hasChildren'
],
]
]
];

View file

@ -0,0 +1,17 @@
<?php
use Kirby\Cms\SiteBlueprint;
/**
* SiteBlueprint
*/
return [
'fields' => [
'name' => fn (SiteBlueprint $blueprint) => $blueprint->name(),
'options' => fn (SiteBlueprint $blueprint) => $blueprint->options(),
'tabs' => fn (SiteBlueprint $blueprint) => $blueprint->tabs(),
'title' => fn (SiteBlueprint $blueprint) => $blueprint->title(),
],
'type' => 'Kirby\Cms\SiteBlueprint',
'views' => [],
];

View file

@ -0,0 +1,98 @@
<?php
use Kirby\Cms\System;
use Kirby\Toolkit\Str;
/**
* System
*/
return [
'fields' => [
'ascii' => fn () => Str::$ascii,
'authStatus' => fn () => $this->kirby()->auth()->status()->toArray(),
'defaultLanguage' => fn () => $this->kirby()->panelLanguage(),
'isOk' => fn (System $system) => $system->isOk(),
'isInstallable' => fn (System $system) => $system->isInstallable(),
'isInstalled' => fn (System $system) => $system->isInstalled(),
'isLocal' => fn (System $system) => $system->isLocal(),
'multilang' => fn () => $this->kirby()->option('languages', false) !== false,
'languages' => fn () => $this->kirby()->languages(),
'license' => fn (System $system) => $system->license(),
'locales' => function () {
$locales = [];
$translations = $this->kirby()->translations();
foreach ($translations as $translation) {
$locales[$translation->code()] = $translation->locale();
}
return $locales;
},
'loginMethods' => fn (System $system) => array_keys($system->loginMethods()),
'requirements' => fn (System $system) => $system->toArray(),
'site' => fn (System $system) => $system->title(),
'slugs' => fn () => Str::$language,
'title' => fn () => $this->site()->title()->value(),
'translation' => function () {
if ($user = $this->user()) {
$translationCode = $user->language();
} else {
$translationCode = $this->kirby()->panelLanguage();
}
if ($translation = $this->kirby()->translation($translationCode)) {
return $translation;
} else {
return $this->kirby()->translation('en');
}
},
'kirbytext' => fn () => $this->kirby()->option('panel.kirbytext') ?? true,
'user' => fn () => $this->user(),
'version' => function () {
$user = $this->user();
if ($user && $user->role()->permissions()->for('access', 'system') === true) {
return $this->kirby()->version();
} else {
return null;
}
}
],
'type' => 'Kirby\Cms\System',
'views' => [
'login' => [
'authStatus',
'isOk',
'isInstallable',
'isInstalled',
'loginMethods',
'title',
'translation'
],
'troubleshooting' => [
'isOk',
'isInstallable',
'isInstalled',
'title',
'translation',
'requirements'
],
'panel' => [
'ascii',
'defaultLanguage',
'isOk',
'isInstalled',
'isLocal',
'kirbytext',
'languages',
'license',
'locales',
'multilang',
'requirements',
'site',
'slugs',
'title',
'translation',
'user' => 'auth',
'version'
]
],
];

View file

@ -0,0 +1,24 @@
<?php
use Kirby\Cms\Translation;
/**
* Translation
*/
return [
'fields' => [
'author' => fn (Translation $translation) => $translation->author(),
'data' => fn (Translation $translation) => $translation->dataWithFallback(),
'direction' => fn (Translation $translation) => $translation->direction(),
'id' => fn (Translation $translation) => $translation->id(),
'name' => fn (Translation $translation) => $translation->name(),
],
'type' => 'Kirby\Cms\Translation',
'views' => [
'compact' => [
'direction',
'id',
'name'
]
]
];

View file

@ -0,0 +1,77 @@
<?php
use Kirby\Cms\User;
use Kirby\Form\Form;
/**
* User
*/
return [
'default' => fn () => $this->user(),
'fields' => [
'avatar' => fn (User $user) => $user->avatar() ? $user->avatar()->crop(512) : null,
'blueprint' => fn (User $user) => $user->blueprint(),
'content' => fn (User $user) => Form::for($user)->values(),
'email' => fn (User $user) => $user->email(),
'files' => fn (User $user) => $user->files()->sorted(),
'id' => fn (User $user) => $user->id(),
'language' => fn (User $user) => $user->language(),
'name' => fn (User $user) => $user->name()->value(),
'next' => fn (User $user) => $user->next(),
'options' => fn (User $user) => $user->panel()->options(),
'panelImage' => fn (User $user) => $user->panel()->image(),
'permissions' => fn (User $user) => $user->role()->permissions()->toArray(),
'prev' => fn (User $user) => $user->prev(),
'role' => fn (User $user) => $user->role(),
'roles' => fn (User $user) => $user->roles(),
'username' => fn (User $user) => $user->username()
],
'type' => 'Kirby\Cms\User',
'views' => [
'default' => [
'avatar',
'content',
'email',
'id',
'language',
'name',
'next' => 'compact',
'options',
'prev' => 'compact',
'role',
'username'
],
'compact' => [
'avatar' => 'compact',
'id',
'email',
'language',
'name',
'role' => 'compact',
'username'
],
'auth' => [
'avatar' => 'compact',
'permissions',
'email',
'id',
'name',
'role',
'language'
],
'panel' => [
'avatar' => 'compact',
'blueprint',
'content',
'email',
'id',
'language',
'name',
'next' => ['id', 'name'],
'options',
'prev' => ['id', 'name'],
'role',
'username',
],
]
];

View file

@ -0,0 +1,18 @@
<?php
use Kirby\Cms\UserBlueprint;
/**
* UserBlueprint
*/
return [
'fields' => [
'name' => fn (UserBlueprint $blueprint) => $blueprint->name(),
'options' => fn (UserBlueprint $blueprint) => $blueprint->options(),
'tabs' => fn (UserBlueprint $blueprint) => $blueprint->tabs(),
'title' => fn (UserBlueprint $blueprint) => $blueprint->title(),
],
'type' => 'Kirby\Cms\UserBlueprint',
'views' => [
],
];

View file

@ -0,0 +1,26 @@
<?php
/**
* Api Routes Definitions
*/
return function ($kirby) {
$routes = array_merge(
include __DIR__ . '/routes/auth.php',
include __DIR__ . '/routes/pages.php',
include __DIR__ . '/routes/roles.php',
include __DIR__ . '/routes/site.php',
include __DIR__ . '/routes/users.php',
include __DIR__ . '/routes/files.php',
include __DIR__ . '/routes/lock.php',
include __DIR__ . '/routes/system.php',
include __DIR__ . '/routes/translations.php'
);
// only add the language routes if the
// multi language setup is activated
if ($kirby->option('languages', false) !== false) {
$routes = array_merge($routes, include __DIR__ . '/routes/languages.php');
}
return $routes;
};

View file

@ -0,0 +1,108 @@
<?php
use Kirby\Exception\InvalidArgumentException;
use Kirby\Exception\NotFoundException;
/**
* Authentication
*/
return [
[
'pattern' => 'auth',
'method' => 'GET',
'action' => function () {
if ($user = $this->kirby()->auth()->user()) {
return $this->resolve($user)->view('auth');
}
throw new NotFoundException('The user cannot be found');
}
],
[
'pattern' => 'auth/code',
'method' => 'POST',
'auth' => false,
'action' => function () {
$auth = $this->kirby()->auth();
// csrf token check
if ($auth->type() === 'session' && $auth->csrf() === false) {
throw new InvalidArgumentException('Invalid CSRF token');
}
$user = $auth->verifyChallenge($this->requestBody('code'));
return [
'code' => 200,
'status' => 'ok',
'user' => $this->resolve($user)->view('auth')->toArray()
];
}
],
[
'pattern' => 'auth/login',
'method' => 'POST',
'auth' => false,
'action' => function () {
$auth = $this->kirby()->auth();
$methods = $this->kirby()->system()->loginMethods();
// csrf token check
if ($auth->type() === 'session' && $auth->csrf() === false) {
throw new InvalidArgumentException('Invalid CSRF token');
}
$email = $this->requestBody('email');
$long = $this->requestBody('long');
$password = $this->requestBody('password');
if ($password) {
if (isset($methods['password']) !== true) {
throw new InvalidArgumentException('Login with password is not enabled');
}
if (
isset($methods['password']['2fa']) === true &&
$methods['password']['2fa'] === true
) {
$status = $auth->login2fa($email, $password, $long);
} else {
$user = $auth->login($email, $password, $long);
}
} else {
if (isset($methods['code']) === true) {
$mode = 'login';
} elseif (isset($methods['password-reset']) === true) {
$mode = 'password-reset';
} else {
throw new InvalidArgumentException('Login without password is not enabled');
}
$status = $auth->createChallenge($email, $long, $mode);
}
if (isset($user)) {
return [
'code' => 200,
'status' => 'ok',
'user' => $this->resolve($user)->view('auth')->toArray()
];
} else {
return [
'code' => 200,
'status' => 'ok',
'challenge' => $status->challenge()
];
}
}
],
[
'pattern' => 'auth/logout',
'method' => 'POST',
'auth' => false,
'action' => function () {
$this->kirby()->auth()->logout();
return true;
}
],
];

View file

@ -0,0 +1,132 @@
<?php
// routing pattern to match all models with files
$pattern = '(account|pages/[^/]+|site|users/[^/]+)';
/**
* Files Routes
*/
return [
[
'pattern' => $pattern . '/files/(:any)/sections/(:any)',
'method' => 'GET',
'action' => function (string $path, string $filename, string $sectionName) {
if ($section = $this->file($path, $filename)->blueprint()->section($sectionName)) {
return $section->toResponse();
}
}
],
[
'pattern' => $pattern . '/files/(:any)/fields/(:any)/(:all?)',
'method' => 'ALL',
'action' => function (string $parent, string $filename, string $fieldName, string $path = null) {
if ($file = $this->file($parent, $filename)) {
return $this->fieldApi($file, $fieldName, $path);
}
}
],
[
'pattern' => $pattern . '/files',
'method' => 'GET',
'action' => function (string $path) {
return $this->parent($path)->files()->sorted();
}
],
[
'pattern' => $pattern . '/files',
'method' => 'POST',
'action' => function (string $path) {
// move_uploaded_file() not working with unit test
// @codeCoverageIgnoreStart
return $this->upload(function ($source, $filename) use ($path) {
return $this->parent($path)->createFile([
'content' => [
'sort' => $this->requestBody('sort')
],
'source' => $source,
'template' => $this->requestBody('template'),
'filename' => $filename
]);
});
// @codeCoverageIgnoreEnd
}
],
[
'pattern' => $pattern . '/files/search',
'method' => 'GET|POST',
'action' => function (string $path) {
$files = $this->parent($path)->files();
if ($this->requestMethod() === 'GET') {
return $files->search($this->requestQuery('q'));
} else {
return $files->query($this->requestBody());
}
}
],
[
'pattern' => $pattern . '/files/sort',
'method' => 'PATCH',
'action' => function (string $path) {
return $this->parent($path)->files()->changeSort(
$this->requestBody('files'),
$this->requestBody('index')
);
}
],
[
'pattern' => $pattern . '/files/(:any)',
'method' => 'GET',
'action' => function (string $path, string $filename) {
return $this->file($path, $filename);
}
],
[
'pattern' => $pattern . '/files/(:any)',
'method' => 'PATCH',
'action' => function (string $path, string $filename) {
return $this->file($path, $filename)->update($this->requestBody(), $this->language(), true);
}
],
[
'pattern' => $pattern . '/files/(:any)',
'method' => 'POST',
'action' => function (string $path, string $filename) {
return $this->upload(function ($source) use ($path, $filename) {
return $this->file($path, $filename)->replace($source);
});
}
],
[
'pattern' => $pattern . '/files/(:any)',
'method' => 'DELETE',
'action' => function (string $path, string $filename) {
return $this->file($path, $filename)->delete();
}
],
[
'pattern' => $pattern . '/files/(:any)/name',
'method' => 'PATCH',
'action' => function (string $path, string $filename) {
return $this->file($path, $filename)->changeName($this->requestBody('name'));
}
],
[
'pattern' => 'files/search',
'method' => 'GET|POST',
'action' => function () {
$files = $this
->site()
->index(true)
->filter('isReadable', true)
->files();
if ($this->requestMethod() === 'GET') {
return $files->search($this->requestQuery('q'));
} else {
return $files->query($this->requestBody());
}
}
],
];

View file

@ -0,0 +1,46 @@
<?php
/**
* Roles Routes
*/
return [
[
'pattern' => 'languages',
'method' => 'GET',
'action' => function () {
return $this->kirby()->languages();
}
],
[
'pattern' => 'languages',
'method' => 'POST',
'action' => function () {
return $this->kirby()->languages()->create($this->requestBody());
}
],
[
'pattern' => 'languages/(:any)',
'method' => 'GET',
'action' => function (string $code) {
return $this->kirby()->languages()->find($code);
}
],
[
'pattern' => 'languages/(:any)',
'method' => 'PATCH',
'action' => function (string $code) {
if ($language = $this->kirby()->languages()->find($code)) {
return $language->update($this->requestBody());
}
}
],
[
'pattern' => 'languages/(:any)',
'method' => 'DELETE',
'action' => function (string $code) {
if ($language = $this->kirby()->languages()->find($code)) {
return $language->delete();
}
}
]
];

View file

@ -0,0 +1,91 @@
<?php
/**
* Content Lock Routes
*/
return [
[
'pattern' => '(:all)/lock',
'method' => 'GET',
/**
* @deprecated 3.6.0
* @todo Remove in 3.7.0
*/
'action' => function (string $path) {
deprecated('The `GET (:all)/lock` API endpoint has been deprecated and will be removed in 3.7.0');
if ($lock = $this->parent($path)->lock()) {
return [
'supported' => true,
'locked' => $lock->get()
];
}
return [
'supported' => false,
'locked' => null
];
}
],
[
'pattern' => '(:all)/lock',
'method' => 'PATCH',
'action' => function (string $path) {
if ($lock = $this->parent($path)->lock()) {
return $lock->create();
}
}
],
[
'pattern' => '(:all)/lock',
'method' => 'DELETE',
'action' => function (string $path) {
if ($lock = $this->parent($path)->lock()) {
return $lock->remove();
}
}
],
[
'pattern' => '(:all)/unlock',
'method' => 'GET',
/**
* @deprecated 3.6.0
* @todo Remove in 3.7.0
*/
'action' => function (string $path) {
deprecated('The `GET (:all)/unlock` API endpoint has been deprecated and will be removed in 3.7.0');
if ($lock = $this->parent($path)->lock()) {
return [
'supported' => true,
'unlocked' => $lock->isUnlocked()
];
}
return [
'supported' => false,
'unlocked' => null
];
}
],
[
'pattern' => '(:all)/unlock',
'method' => 'PATCH',
'action' => function (string $path) {
if ($lock = $this->parent($path)->lock()) {
return $lock->unlock();
}
}
],
[
'pattern' => '(:all)/unlock',
'method' => 'DELETE',
'action' => function (string $path) {
if ($lock = $this->parent($path)->lock()) {
return $lock->resolve();
}
}
],
];

View file

@ -0,0 +1,132 @@
<?php
/**
* Page Routes
*/
return [
[
'pattern' => 'pages/(:any)',
'method' => 'GET',
'action' => function (string $id) {
return $this->page($id);
}
],
[
'pattern' => 'pages/(:any)',
'method' => 'PATCH',
'action' => function (string $id) {
return $this->page($id)->update($this->requestBody(), $this->language(), true);
}
],
[
'pattern' => 'pages/(:any)',
'method' => 'DELETE',
'action' => function (string $id) {
return $this->page($id)->delete($this->requestBody('force', false));
}
],
[
'pattern' => 'pages/(:any)/blueprint',
'method' => 'GET',
'action' => function (string $id) {
return $this->page($id)->blueprint();
}
],
[
'pattern' => [
'pages/(:any)/blueprints',
/**
* @deprecated
* @todo remove in 3.7.0
*/
'pages/(:any)/children/blueprints',
],
'method' => 'GET',
'action' => function (string $id) {
// @codeCoverageIgnoreStart
if ($this->route->pattern() === 'pages/([a-zA-Z0-9\.\-_%= \+\@\(\)]+)/children/blueprints') {
deprecated('`GET pages/(:any)/children/blueprints` API endpoint has been deprecated and will be removed in 3.7.0. Use `GET pages/(:any)/blueprints` instead');
}
// @codeCoverageIgnoreEnd
return $this->page($id)->blueprints($this->requestQuery('section'));
}
],
[
'pattern' => 'pages/(:any)/children',
'method' => 'GET',
'action' => function (string $id) {
return $this->pages($id, $this->requestQuery('status'));
}
],
[
'pattern' => 'pages/(:any)/children',
'method' => 'POST',
'action' => function (string $id) {
return $this->page($id)->createChild($this->requestBody());
}
],
[
'pattern' => 'pages/(:any)/children/search',
'method' => 'GET|POST',
'action' => function (string $id) {
return $this->searchPages($id);
}
],
[
'pattern' => 'pages/(:any)/duplicate',
'method' => 'POST',
'action' => function (string $id) {
return $this->page($id)->duplicate($this->requestBody('slug'), [
'children' => $this->requestBody('children'),
'files' => $this->requestBody('files'),
]);
}
],
[
'pattern' => 'pages/(:any)/slug',
'method' => 'PATCH',
'action' => function (string $id) {
return $this->page($id)->changeSlug($this->requestBody('slug'));
}
],
[
'pattern' => 'pages/(:any)/status',
'method' => 'PATCH',
'action' => function (string $id) {
return $this->page($id)->changeStatus($this->requestBody('status'), $this->requestBody('position'));
}
],
[
'pattern' => 'pages/(:any)/template',
'method' => 'PATCH',
'action' => function (string $id) {
return $this->page($id)->changeTemplate($this->requestBody('template'));
}
],
[
'pattern' => 'pages/(:any)/title',
'method' => 'PATCH',
'action' => function (string $id) {
return $this->page($id)->changeTitle($this->requestBody('title'));
}
],
[
'pattern' => 'pages/(:any)/sections/(:any)',
'method' => 'GET',
'action' => function (string $id, string $sectionName) {
if ($section = $this->page($id)->blueprint()->section($sectionName)) {
return $section->toResponse();
}
}
],
[
'pattern' => 'pages/(:any)/fields/(:any)/(:all?)',
'method' => 'ALL',
'action' => function (string $id, string $fieldName, string $path = null) {
if ($page = $this->page($id)) {
return $this->fieldApi($page, $fieldName, $path);
}
}
],
];

View file

@ -0,0 +1,28 @@
<?php
/**
* Roles Routes
*/
return [
[
'pattern' => 'roles',
'method' => 'GET',
'action' => function () {
switch (get('canBe')) {
case 'changed':
return $this->kirby()->roles()->canBeChanged();
case 'created':
return $this->kirby()->roles()->canBeCreated();
default:
return $this->kirby()->roles();
}
}
],
[
'pattern' => 'roles/(:any)',
'method' => 'GET',
'action' => function (string $name) {
return $this->kirby()->roles()->find($name);
}
]
];

View file

@ -0,0 +1,115 @@
<?php
/**
* Site Routes
*/
return [
[
'pattern' => 'site',
'action' => function () {
return $this->site();
}
],
[
'pattern' => 'site',
'method' => 'PATCH',
'action' => function () {
return $this->site()->update($this->requestBody(), $this->language(), true);
}
],
[
'pattern' => 'site/children',
'method' => 'GET',
'action' => function () {
return $this->pages(null, $this->requestQuery('status'));
}
],
[
'pattern' => 'site/children',
'method' => 'POST',
'action' => function () {
return $this->site()->createChild($this->requestBody());
}
],
[
'pattern' => 'site/children/search',
'method' => 'GET|POST',
'action' => function () {
return $this->searchPages();
}
],
[
'pattern' => 'site/blueprint',
'method' => 'GET',
'action' => function () {
return $this->site()->blueprint();
}
],
[
'pattern' => [
'site/blueprints',
/**
* @deprecated
* @todo remove in 3.7.0
*/
'site/children/blueprints',
],
'method' => 'GET',
'action' => function () {
// @codeCoverageIgnoreStart
if ($this->route->pattern() === 'site/children/blueprints') {
deprecated('`GET site/children/blueprints` API endpoint has been deprecated and will be removed in 3.7.0. Use `GET site/blueprints` instead.');
}
// @codeCoverageIgnoreEnd
return $this->site()->blueprints($this->requestQuery('section'));
}
],
[
'pattern' => 'site/find',
'method' => 'POST',
'action' => function () {
return $this->site()->find(false, ...$this->requestBody());
}
],
[
'pattern' => 'site/title',
'method' => 'PATCH',
'action' => function () {
return $this->site()->changeTitle($this->requestBody('title'));
}
],
[
'pattern' => 'site/search',
'method' => 'GET|POST',
'action' => function () {
$pages = $this
->site()
->index(true)
->filter('isReadable', true);
if ($this->requestMethod() === 'GET') {
return $pages->search($this->requestQuery('q'));
} else {
return $pages->query($this->requestBody());
}
}
],
[
'pattern' => 'site/sections/(:any)',
'method' => 'GET',
'action' => function (string $sectionName) {
if ($section = $this->site()->blueprint()->section($sectionName)) {
return $section->toResponse();
}
}
],
[
'pattern' => 'site/fields/(:any)/(:all?)',
'method' => 'ALL',
'action' => function (string $fieldName, string $path = null) {
return $this->fieldApi($this->site(), $fieldName, $path);
}
]
];

View file

@ -0,0 +1,79 @@
<?php
use Kirby\Exception\Exception;
use Kirby\Exception\InvalidArgumentException;
/**
* System Routes
*/
return [
[
'pattern' => 'system',
'method' => 'GET',
'auth' => false,
'action' => function () {
$system = $this->kirby()->system();
if ($this->kirby()->user()) {
return $system;
} else {
if ($system->isOk() === true) {
$info = $this->resolve($system)->view('login')->toArray();
} else {
$info = $this->resolve($system)->view('troubleshooting')->toArray();
}
return [
'status' => 'ok',
'data' => $info,
'type' => 'model'
];
}
}
],
[
'pattern' => 'system/register',
'method' => 'POST',
'action' => function () {
return $this->kirby()->system()->register($this->requestBody('license'), $this->requestBody('email'));
}
],
[
'pattern' => 'system/install',
'method' => 'POST',
'auth' => false,
'action' => function () {
$system = $this->kirby()->system();
$auth = $this->kirby()->auth();
// csrf token check
if ($auth->type() === 'session' && $auth->csrf() === false) {
throw new InvalidArgumentException('Invalid CSRF token');
}
if ($system->isOk() === false) {
throw new Exception('The server is not setup correctly');
}
if ($system->isInstallable() === false) {
throw new Exception('The Panel cannot be installed');
}
if ($system->isInstalled() === true) {
throw new Exception('The Panel is already installed');
}
// create the first user
$user = $this->users()->create($this->requestBody());
$token = $user->login($this->requestBody('password'));
return [
'status' => 'ok',
'token' => $token,
'user' => $this->resolve($user)->view('auth')->toArray()
];
}
]
];

View file

@ -0,0 +1,24 @@
<?php
/**
* Translations Routes
*/
return [
[
'pattern' => 'translations',
'method' => 'GET',
'auth' => false,
'action' => function () {
return $this->kirby()->translations();
}
],
[
'pattern' => 'translations/(:any)',
'method' => 'GET',
'auth' => false,
'action' => function (string $code) {
return $this->kirby()->translations()->find($code);
}
]
];

View file

@ -0,0 +1,207 @@
<?php
use Kirby\Filesystem\F;
/**
* User Routes
*/
return [
[
'pattern' => 'users',
'method' => 'GET',
'action' => function () {
return $this->users()->sort('username', 'asc', 'email', 'asc');
}
],
[
'pattern' => 'users',
'method' => 'POST',
'action' => function () {
return $this->users()->create($this->requestBody());
}
],
[
'pattern' => 'users/search',
'method' => 'GET|POST',
'action' => function () {
if ($this->requestMethod() === 'GET') {
return $this->users()->search($this->requestQuery('q'));
} else {
return $this->users()->query($this->requestBody());
}
}
],
[
'pattern' => [
'(account)',
'users/(:any)',
],
'method' => 'GET',
'action' => function (string $id) {
return $this->user($id);
}
],
[
'pattern' => [
'(account)',
'users/(:any)',
],
'method' => 'PATCH',
'action' => function (string $id) {
return $this->user($id)->update($this->requestBody(), $this->language(), true);
}
],
[
'pattern' => [
'(account)',
'users/(:any)',
],
'method' => 'DELETE',
'action' => function (string $id) {
return $this->user($id)->delete();
}
],
[
'pattern' => [
'(account)/avatar',
'users/(:any)/avatar',
],
'method' => 'GET',
'action' => function (string $id) {
return $this->user($id)->avatar();
}
],
// @codeCoverageIgnoreStart
[
'pattern' => [
'(account)/avatar',
'users/(:any)/avatar',
],
'method' => 'POST',
'action' => function (string $id) {
if ($avatar = $this->user($id)->avatar()) {
$avatar->delete();
}
return $this->upload(function ($source, $filename) use ($id) {
return $this->user($id)->createFile([
'filename' => 'profile.' . F::extension($filename),
'template' => 'avatar',
'source' => $source
]);
}, $single = true);
}
],
// @codeCoverageIgnoreEnd
[
'pattern' => [
'(account)/avatar',
'users/(:any)/avatar',
],
'method' => 'DELETE',
'action' => function (string $id) {
return $this->user($id)->avatar()->delete();
}
],
[
'pattern' => [
'(account)/blueprint',
'users/(:any)/blueprint',
],
'method' => 'GET',
'action' => function (string $id) {
return $this->user($id)->blueprint();
}
],
[
'pattern' => [
'(account)/blueprints',
'users/(:any)/blueprints',
],
'method' => 'GET',
'action' => function (string $id) {
return $this->user($id)->blueprints($this->requestQuery('section'));
}
],
[
'pattern' => [
'(account)/email',
'users/(:any)/email',
],
'method' => 'PATCH',
'action' => function (string $id) {
return $this->user($id)->changeEmail($this->requestBody('email'));
}
],
[
'pattern' => [
'(account)/language',
'users/(:any)/language',
],
'method' => 'PATCH',
'action' => function (string $id) {
return $this->user($id)->changeLanguage($this->requestBody('language'));
}
],
[
'pattern' => [
'(account)/name',
'users/(:any)/name',
],
'method' => 'PATCH',
'action' => function (string $id) {
return $this->user($id)->changeName($this->requestBody('name'));
}
],
[
'pattern' => [
'(account)/password',
'users/(:any)/password',
],
'method' => 'PATCH',
'action' => function (string $id) {
return $this->user($id)->changePassword($this->requestBody('password'));
}
],
[
'pattern' => [
'(account)/role',
'users/(:any)/role',
],
'method' => 'PATCH',
'action' => function (string $id) {
return $this->user($id)->changeRole($this->requestBody('role'));
}
],
[
'pattern' => [
'(account)/roles',
'users/(:any)/roles',
],
'action' => function (string $id) {
return $this->user($id)->roles();
}
],
[
'pattern' => [
'(account)/sections/(:any)',
'users/(:any)/sections/(:any)',
],
'method' => 'GET',
'action' => function (string $id, string $sectionName) {
if ($section = $this->user($id)->blueprint()->section($sectionName)) {
return $section->toResponse();
}
}
],
[
'pattern' => [
'(account)/fields/(:any)/(:all?)',
'users/(:any)/fields/(:any)/(:all?)',
],
'method' => 'ALL',
'action' => function (string $id, string $fieldName, string $path = null) {
return $this->fieldApi($this->user($id), $fieldName, $path);
}
],
];

View file

@ -0,0 +1,12 @@
<?php
return function () {
return [
'icon' => 'account',
'label' => t('view.account'),
'search' => 'users',
'dialogs' => require __DIR__ . '/account/dialogs.php',
'dropdowns' => require __DIR__ . '/account/dropdowns.php',
'views' => require __DIR__ . '/account/views.php'
];
};

View file

@ -0,0 +1,70 @@
<?php
$dialogs = require __DIR__ . '/../users/dialogs.php';
return [
// change email
'account.changeEmail' => [
'pattern' => '(account)/changeEmail',
'load' => $dialogs['user.changeEmail']['load'],
'submit' => $dialogs['user.changeEmail']['submit'],
],
// change language
'account.changeLanguage' => [
'pattern' => '(account)/changeLanguage',
'load' => $dialogs['user.changeLanguage']['load'],
'submit' => $dialogs['user.changeLanguage']['submit'],
],
// change name
'account.changeName' => [
'pattern' => '(account)/changeName',
'load' => $dialogs['user.changeName']['load'],
'submit' => $dialogs['user.changeName']['submit'],
],
// change password
'account.changePassword' => [
'pattern' => '(account)/changePassword',
'load' => $dialogs['user.changePassword']['load'],
'submit' => $dialogs['user.changePassword']['submit'],
],
// change role
'account.changeRole' => [
'pattern' => '(account)/changeRole',
'load' => $dialogs['user.changeRole']['load'],
'submit' => $dialogs['user.changeRole']['submit'],
],
// delete
'account.delete' => [
'pattern' => '(account)/delete',
'load' => $dialogs['user.delete']['load'],
'submit' => $dialogs['user.delete']['submit'],
],
// change file name
'account.file.changeName' => [
'pattern' => '(account)/files/(:any)/changeName',
'load' => $dialogs['user.file.changeName']['load'],
'submit' => $dialogs['user.file.changeName']['submit'],
],
// change file sort
'account.file.changeSort' => [
'pattern' => '(account)/files/(:any)/changeSort',
'load' => $dialogs['user.file.changeSort']['load'],
'submit' => $dialogs['user.file.changeSort']['submit'],
],
// delete
'account.file.delete' => [
'pattern' => '(account)/files/(:any)/delete',
'load' => $dialogs['user.file.delete']['load'],
'submit' => $dialogs['user.file.delete']['submit'],
],
];

View file

@ -0,0 +1,14 @@
<?php
$dropdowns = require __DIR__ . '/../users/dropdowns.php';
return [
'account' => [
'pattern' => '(account)',
'options' => $dropdowns['user']['options']
],
'account.file' => [
'pattern' => '(account)/files/(:any)',
'options' => $dropdowns['user.file']['options']
],
];

View file

@ -0,0 +1,34 @@
<?php
use Kirby\Cms\Find;
use Kirby\Panel\Panel;
return [
'account' => [
'pattern' => 'account',
'action' => fn () => [
'component' => 'k-account-view',
'props' => kirby()->user()->panel()->props(),
],
],
'account.file' => [
'pattern' => 'account/files/(:any)',
'action' => function (string $filename) {
return Find::file('account', $filename)->panel()->view();
}
],
'account.logout' => [
'pattern' => 'logout',
'auth' => false,
'action' => function () {
if ($user = kirby()->user()) {
$user->logout();
}
Panel::go('login');
},
],
'account.password' => [
'pattern' => 'reset-password',
'action' => fn () => ['component' => 'k-reset-password-view']
]
];

View file

@ -0,0 +1,131 @@
<?php
use Kirby\Cms\Find;
use Kirby\Panel\Field;
use Kirby\Panel\Panel;
use Kirby\Toolkit\Escape;
/**
* Shared file dialogs
* They are included in the site and
* users area to create dialogs there.
* The array keys are replaced by
* the appropriate routes in the areas.
*/
return [
'changeName' => [
'load' => function (string $path, string $filename) {
$file = Find::file($path, $filename);
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'name' => [
'label' => t('name'),
'type' => 'slug',
'required' => true,
'icon' => 'title',
'allow' => '@._-',
'after' => '.' . $file->extension(),
'preselect' => true
]
],
'submitButton' => t('rename'),
'value' => [
'name' => $file->name(),
]
]
];
},
'submit' => function (string $path, string $filename) {
$file = Find::file($path, $filename);
$renamed = $file->changeName(get('name'));
$oldUrl = $file->panel()->url(true);
$newUrl = $renamed->panel()->url(true);
$response = [
'event' => 'file.changeName',
'dispatch' => [
'content/move' => [
$oldUrl,
$newUrl
]
],
];
// check for a necessary redirect after the filename has changed
if (Panel::referrer() === $oldUrl && $oldUrl !== $newUrl) {
$response['redirect'] = $newUrl;
}
return $response;
}
],
'changeSort' => [
'load' => function (string $path, string $filename) {
$file = Find::file($path, $filename);
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'position' => Field::filePosition($file)
],
'submitButton' => t('change'),
'value' => [
'position' => $file->sort()->isEmpty() ? $file->siblings(false)->count() + 1 : $file->sort()->toInt(),
]
]
];
},
'submit' => function (string $path, string $filename) {
$file = Find::file($path, $filename);
$files = $file->siblings()->sorted();
$ids = $files->keys();
$newIndex = (int)(get('position')) - 1;
$oldIndex = $files->indexOf($file);
array_splice($ids, $oldIndex, 1);
array_splice($ids, $newIndex, 0, $file->id());
$files->changeSort($ids);
return [
'event' => 'file.sort',
];
}
],
'delete' => [
'load' => function (string $path, string $filename) {
$file = Find::file($path, $filename);
return [
'component' => 'k-remove-dialog',
'props' => [
'text' => tt('file.delete.confirm', [
'filename' => Escape::html($file->filename())
]),
]
];
},
'submit' => function (string $path, string $filename) {
$file = Find::file($path, $filename);
$redirect = false;
$referrer = Panel::referrer();
$url = $file->panel()->url(true);
$file->delete();
// redirect to the parent model URL
// if the dialog has been opened in the file view
if ($referrer === $url) {
$redirect = $file->parent()->panel()->url(true);
}
return [
'event' => 'file.delete',
'dispatch' => ['content/remove' => [$url]],
'redirect' => $redirect
];
}
],
];

View file

@ -0,0 +1,9 @@
<?php
use Kirby\Cms\Find;
return [
'file' => function (string $parent, string $filename) {
return Find::file($parent, $filename)->panel()->dropdown();
}
];

View file

@ -0,0 +1,39 @@
<?php
use Kirby\Panel\Panel;
return function ($kirby) {
return [
'icon' => 'settings',
'label' => t('view.installation'),
'views' => [
'installation' => [
'pattern' => 'installation',
'auth' => false,
'action' => function () use ($kirby) {
$system = $kirby->system();
return [
'component' => 'k-installation-view',
'props' => [
'isInstallable' => $system->isInstallable(),
'isInstalled' => $system->isInstalled(),
'isOk' => $system->isOk(),
'requirements' => $system->status(),
'translations' => $kirby->translations()->values(function ($translation) {
return [
'text' => $translation->name(),
'value' => $translation->code(),
];
}),
]
];
}
],
'installation.fallback' => [
'pattern' => '(:all)',
'auth' => false,
'action' => fn () => Panel::go('installation')
]
]
];
};

View file

@ -0,0 +1,11 @@
<?php
return function ($kirby) {
return [
'icon' => 'globe',
'label' => t('view.languages'),
'menu' => true,
'dialogs' => require __DIR__ . '/languages/dialogs.php',
'views' => require __DIR__ . '/languages/views.php'
];
};

View file

@ -0,0 +1,149 @@
<?php
use Kirby\Cms\Find;
use Kirby\Panel\Field;
use Kirby\Toolkit\A;
use Kirby\Toolkit\Escape;
$languageDialogFields = [
'name' => [
'label' => t('language.name'),
'type' => 'text',
'required' => true,
'icon' => 'title'
],
'code' => [
'label' => t('language.code'),
'type' => 'text',
'required' => true,
'counter' => false,
'icon' => 'globe',
'width' => '1/2'
],
'direction' => [
'label' => t('language.direction'),
'type' => 'select',
'required' => true,
'empty' => false,
'options' => [
['value' => 'ltr', 'text' => t('language.direction.ltr')],
['value' => 'rtl', 'text' => t('language.direction.rtl')]
],
'width' => '1/2'
],
'locale' => [
'label' => t('language.locale'),
'type' => 'text',
],
];
return [
// create language
'language.create' => [
'pattern' => 'languages/create',
'load' => function () use ($languageDialogFields) {
return [
'component' => 'k-language-dialog',
'props' => [
'fields' => $languageDialogFields,
'submitButton' => t('language.create'),
'value' => [
'code' => '',
'direction' => 'ltr',
'locale' => '',
'name' => '',
]
]
];
},
'submit' => function () {
kirby()->languages()->create([
'code' => get('code'),
'direction' => get('direction'),
'locale' => get('locale'),
'name' => get('name'),
]);
return [
'event' => 'language.create'
];
}
],
// delete language
'language.delete' => [
'pattern' => 'languages/(:any)/delete',
'load' => function (string $id) {
$language = Find::language($id);
return [
'component' => 'k-remove-dialog',
'props' => [
'text' => tt('language.delete.confirm', [
'name' => Escape::html($language->name())
])
]
];
},
'submit' => function (string $id) {
Find::language($id)->delete();
return [
'event' => 'language.delete',
];
}
],
// update language
'language.update' => [
'pattern' => 'languages/(:any)/update',
'load' => function (string $id) use ($languageDialogFields) {
$language = Find::language($id);
$fields = $languageDialogFields;
$locale = $language->locale();
// use the first locale key if there's only one
if (count($locale) === 1) {
$locale = A::first($locale);
}
// the code of an existing language cannot be changed
$fields['code']['disabled'] = true;
// if the locale settings is more complex than just a
// single string, the text field won't do it anymore.
// Changes can only be made in the language file and
// we display a warning box instead.
if (is_array($locale) === true) {
$fields['locale'] = [
'label' => $fields['locale']['label'],
'type' => 'info',
'text' => t('language.locale.warning')
];
}
return [
'component' => 'k-language-dialog',
'props' => [
'fields' => $fields,
'submitButton' => t('save'),
'value' => [
'code' => $language->code(),
'direction' => $language->direction(),
'locale' => $locale,
'name' => $language->name(),
'rules' => $language->rules(),
]
]
];
},
'submit' => function (string $id) {
$language = Find::language($id)->update([
'direction' => get('direction'),
'locale' => get('locale'),
'name' => get('name'),
]);
return [
'event' => 'language.update'
];
}
],
];

View file

@ -0,0 +1,24 @@
<?php
use Kirby\Toolkit\Escape;
return [
'languages' => [
'pattern' => 'languages',
'action' => function () {
$kirby = kirby();
return [
'component' => 'k-languages-view',
'props' => [
'languages' => $kirby->languages()->values(fn ($language) => [
'default' => $language->isDefault(),
'id' => $language->code(),
'info' => Escape::html($language->code()),
'text' => Escape::html($language->name()),
])
]
];
}
],
];

View file

@ -0,0 +1,43 @@
<?php
use Kirby\Panel\Panel;
return function ($kirby) {
return [
'icon' => 'user',
'label' => t('login'),
'views' => [
'login' => [
'pattern' => 'login',
'auth' => false,
'action' => function () use ($kirby) {
$system = $kirby->system();
$status = $kirby->auth()->status();
return [
'component' => 'k-login-view',
'props' => [
'methods' => array_keys($system->loginMethods()),
'pending' => [
'email' => $status->email(),
'challenge' => $status->challenge()
]
],
];
}
],
'login.fallback' => [
'pattern' => '(:all)',
'auth' => false,
'action' => function ($path) use ($kirby) {
/**
* Store the current path in the session
* Once the user is logged in, the path will
* be used to redirect to that view again
*/
$kirby->session()->set('panel.path', $path);
Panel::go('login');
}
]
]
];
};

View file

@ -0,0 +1,17 @@
<?php
return function ($kirby) {
return [
'breadcrumbLabel' => function () use ($kirby) {
return $kirby->site()->title()->or(t('view.site'))->toString();
},
'icon' => 'home',
'label' => $kirby->site()->blueprint()->title() ?? t('view.site'),
'menu' => true,
'dialogs' => require __DIR__ . '/site/dialogs.php',
'dropdowns' => require __DIR__ . '/site/dropdowns.php',
'searches' => require __DIR__ . '/site/searches.php',
'views' => require __DIR__ . '/site/views.php',
];
};

View file

@ -0,0 +1,551 @@
<?php
use Kirby\Cms\Find;
use Kirby\Exception\Exception;
use Kirby\Exception\InvalidArgumentException;
use Kirby\Exception\PermissionException;
use Kirby\Panel\Field;
use Kirby\Panel\Panel;
use Kirby\Toolkit\Str;
$files = require __DIR__ . '/../files/dialogs.php';
return [
// change page position
'page.changeSort' => [
'pattern' => 'pages/(:any)/changeSort',
'load' => function (string $id) {
$page = Find::page($id);
$position = null;
if ($page->blueprint()->num() !== 'default') {
throw new PermissionException([
'key' => 'page.sort.permission',
'data' => [
'slug' => $page->slug()
]
]);
}
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'position' => Field::pagePosition($page),
],
'submitButton' => t('change'),
'value' => [
'position' => $page->panel()->position()
]
]
];
},
'submit' => function (string $id) {
Find::page($id)->changeStatus('listed', get('position'));
return [
'event' => 'page.sort',
];
}
],
// change page status
'page.changeStatus' => [
'pattern' => 'pages/(:any)/changeStatus',
'load' => function (string $id) {
$page = Find::page($id);
$blueprint = $page->blueprint();
$status = $page->status();
$states = [];
$position = null;
foreach ($blueprint->status() as $key => $state) {
$states[] = [
'value' => $key,
'text' => $state['label'],
'info' => $state['text'],
];
}
if ($status === 'draft') {
$errors = $page->errors();
// switch to the error dialog if there are
// errors and the draft cannot be published
if (count($errors) > 0) {
return [
'component' => 'k-error-dialog',
'props' => [
'message' => t('error.page.changeStatus.incomplete'),
'details' => $errors,
]
];
}
}
$fields = [
'status' => [
'label' => t('page.changeStatus.select'),
'type' => 'radio',
'required' => true,
'options' => $states
]
];
if ($blueprint->num() === 'default') {
$fields['position'] = Field::pagePosition($page, [
'when' => [
'status' => 'listed'
]
]);
$position = $page->panel()->position();
}
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => $fields,
'submitButton' => t('change'),
'value' => [
'status' => $status,
'position' => $position
]
]
];
},
'submit' => function (string $id) {
Find::page($id)->changeStatus(get('status'), get('position'));
return [
'event' => 'page.changeStatus',
];
}
],
// change template
'page.changeTemplate' => [
'pattern' => 'pages/(:any)/changeTemplate',
'load' => function (string $id) {
$page = Find::page($id);
$blueprints = $page->blueprints();
if (count($blueprints) <= 1) {
throw new Exception([
'key' => 'page.changeTemplate.invalid',
'data' => [
'slug' => $id
]
]);
}
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'template' => Field::template($blueprints, [
'required' => true
])
],
'submitButton' => t('change'),
'value' => [
'template' => $page->intendedTemplate()->name()
]
]
];
},
'submit' => function (string $id) {
Find::page($id)->changeTemplate(get('template'));
return [
'event' => 'page.changeTemplate',
];
}
],
// change title
'page.changeTitle' => [
'pattern' => 'pages/(:any)/changeTitle',
'load' => function (string $id) {
$page = Find::page($id);
$permissions = $page->permissions();
$select = get('select', 'title');
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'title' => Field::title([
'required' => true,
'preselect' => $select === 'title',
'disabled' => $permissions->can('changeTitle') === false
]),
'slug' => Field::slug([
'required' => true,
'preselect' => $select === 'slug',
'path' => $page->parent() ? '/' . $page->parent()->id() . '/' : '/',
'disabled' => $permissions->can('changeSlug') === false,
'wizard' => [
'text' => t('page.changeSlug.fromTitle'),
'field' => 'title'
]
])
],
'autofocus' => false,
'submitButton' => t('change'),
'value' => [
'title' => $page->title()->value(),
'slug' => $page->slug(),
]
]
];
},
'submit' => function (string $id) {
$page = Find::page($id);
$title = trim(get('title', ''));
$slug = trim(get('slug', ''));
// basic input validation before we move on
if (Str::length($title) === 0) {
throw new InvalidArgumentException([
'key' => 'page.changeTitle.empty'
]);
}
if (Str::length($slug) === 0) {
throw new InvalidArgumentException([
'key' => 'page.slug.invalid'
]);
}
// nothing changed
if ($page->title()->value() === $title && $page->slug() === $slug) {
return true;
}
// prepare the response
$response = [
'event' => []
];
// the page title changed
if ($page->title()->value() !== $title) {
$page->changeTitle($title);
$response['event'][] = 'page.changeTitle';
}
// the slug changed
if ($page->slug() !== $slug) {
$newPage = $page->changeSlug($slug);
$response['event'][] = 'page.changeSlug';
$response['dispatch'] = [
'content/move' => [
$oldUrl = $page->panel()->url(true),
$newUrl = $newPage->panel()->url(true)
]
];
// check for a necessary redirect after the slug has changed
if (Panel::referrer() === $oldUrl && $oldUrl !== $newUrl) {
$response['redirect'] = $newUrl;
}
}
return $response;
}
],
// create a new page
'page.create' => [
'pattern' => 'pages/create',
'load' => function () {
// the parent model for the new page
$parent = get('parent', 'site');
// the view on which the add button is located
// this is important to find the right section
// and provide the correct templates for the new page
$view = get('view', $parent);
// templates will be fetched depending on the
// section settings in the blueprint
$section = get('section');
// this is the parent model
$model = Find::parent($parent);
// this is the view model
// i.e. site if the add button is on
// the dashboard
$view = Find::parent($view);
// available blueprints/templates for the new page
// are always loaded depending on the matching section
// in the view model blueprint
$blueprints = $view->blueprints($section);
// the pre-selected template
$template = $blueprints[0]['name'] ?? $blueprints[0]['value'] ?? null;
$fields = [
'parent' => Field::hidden(),
'title' => Field::title([
'required' => true,
'preselect' => true
]),
'slug' => Field::slug([
'required' => true,
'sync' => 'title',
'path' => empty($model->id()) === false ? '/' . $model->id() . '/' : '/'
]),
'template' => Field::hidden()
];
// only show template field if > 1 templates available
// or when in debug mode
if (count($blueprints) > 1 || option('debug') === true) {
$fields['template'] = Field::template($blueprints, [
'required' => true
]);
}
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => $fields,
'submitButton' => t('page.draft.create'),
'value' => [
'parent' => $parent,
'slug' => '',
'template' => $template,
'title' => '',
]
]
];
},
'submit' => function () {
$title = trim(get('title', ''));
if (Str::length($title) === 0) {
throw new InvalidArgumentException([
'key' => 'page.changeTitle.empty'
]);
}
$page = Find::parent(get('parent', 'site'))->createChild([
'content' => ['title' => $title],
'slug' => get('slug'),
'template' => get('template'),
]);
return [
'event' => 'page.create',
'redirect' => $page->panel()->url(true)
];
}
],
// delete page
'page.delete' => [
'pattern' => 'pages/(:any)/delete',
'load' => function (string $id) {
$page = Find::page($id);
$text = tt('page.delete.confirm', [
'title' => Escape::html($page->title()->value())
]);
if ($page->childrenAndDrafts()->count() > 0) {
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'info' => [
'type' => 'info',
'theme' => 'negative',
'text' => t('page.delete.confirm.subpages')
],
'check' => [
'label' => t('page.delete.confirm.title'),
'type' => 'text',
'counter' => false
]
],
'size' => 'medium',
'submitButton' => t('delete'),
'text' => $text,
'theme' => 'negative',
]
];
}
return [
'component' => 'k-remove-dialog',
'props' => [
'text' => $text
]
];
},
'submit' => function (string $id) {
$page = Find::page($id);
$redirect = false;
$referrer = Panel::referrer();
$url = $page->panel()->url(true);
if ($page->childrenAndDrafts()->count() > 0 && get('check') !== $page->title()->value()) {
throw new InvalidArgumentException(['key' => 'page.delete.confirm']);
}
$page->delete(true);
// redirect to the parent model URL
// if the dialog has been opened in the page view
if ($referrer === $url) {
$redirect = $page->parentModel()->panel()->url(true);
}
return [
'event' => 'page.delete',
'dispatch' => ['content/remove' => [$url]],
'redirect' => $redirect
];
}
],
// duplicate page
'page.duplicate' => [
'pattern' => 'pages/(:any)/duplicate',
'load' => function (string $id) {
$page = Find::page($id);
$hasChildren = $page->hasChildren();
$hasFiles = $page->hasFiles();
$toggleWidth = '1/' . count(array_filter([$hasChildren, $hasFiles]));
$fields = [
'title' => Field::title([
'required' => true
]),
'slug' => Field::slug([
'required' => true,
'path' => $page->parent() ? '/' . $page->parent()->id() . '/' : '/',
'wizard' => [
'text' => t('page.changeSlug.fromTitle'),
'field' => 'title'
]
])
];
if ($hasFiles === true) {
$fields['files'] = [
'label' => t('page.duplicate.files'),
'type' => 'toggle',
'required' => true,
'width' => $toggleWidth
];
}
if ($hasChildren === true) {
$fields['children'] = [
'label' => t('page.duplicate.pages'),
'type' => 'toggle',
'required' => true,
'width' => $toggleWidth
];
}
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => $fields,
'submitButton' => t('duplicate'),
'value' => [
'children' => false,
'files' => false,
'slug' => $page->slug() . '-' . Str::slug(t('page.duplicate.appendix')),
'title' => $page->title() . ' ' . t('page.duplicate.appendix')
]
]
];
},
'submit' => function (string $id) {
$newPage = Find::page($id)->duplicate(get('slug'), [
'children' => (bool)get('children'),
'files' => (bool)get('files'),
'title' => (string)get('title'),
]);
return [
'event' => 'page.duplicate',
'redirect' => $newPage->panel()->url(true)
];
}
],
// change filename
'page.file.changeName' => [
'pattern' => '(pages/.*?)/files/(:any)/changeName',
'load' => $files['changeName']['load'],
'submit' => $files['changeName']['submit'],
],
// change sort
'page.file.changeSort' => [
'pattern' => '(pages/.*?)/files/(:any)/changeSort',
'load' => $files['changeSort']['load'],
'submit' => $files['changeSort']['submit'],
],
// delete
'page.file.delete' => [
'pattern' => '(pages/.*?)/files/(:any)/delete',
'load' => $files['delete']['load'],
'submit' => $files['delete']['submit'],
],
// change site title
'site.changeTitle' => [
'pattern' => 'site/changeTitle',
'load' => function () {
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'title' => Field::title([
'required' => true,
'preselect' => true
])
],
'submitButton' => t('rename'),
'value' => [
'title' => site()->title()->value()
]
]
];
},
'submit' => function () {
site()->changeTitle(get('title'));
return [
'event' => 'site.changeTitle',
];
}
],
// change filename
'site.file.changeName' => [
'pattern' => '(site)/files/(:any)/changeName',
'load' => $files['changeName']['load'],
'submit' => $files['changeName']['submit'],
],
// change sort
'site.file.changeSort' => [
'pattern' => '(site)/files/(:any)/changeSort',
'load' => $files['changeSort']['load'],
'submit' => $files['changeSort']['submit'],
],
// delete
'site.file.delete' => [
'pattern' => '(site)/files/(:any)/delete',
'load' => $files['delete']['load'],
'submit' => $files['delete']['submit'],
],
];

View file

@ -0,0 +1,26 @@
<?php
use Kirby\Panel\Dropdown;
$files = require __DIR__ . '/../files/dropdowns.php';
return [
'changes' => [
'pattern' => 'changes',
'options' => fn () => Dropdown::changes()
],
'page' => [
'pattern' => 'pages/(:any)',
'options' => function (string $path) {
return Find::page($path)->panel()->dropdown();
}
],
'page.file' => [
'pattern' => '(pages/.*?)/files/(:any)',
'options' => $files['file']
],
'site.file' => [
'pattern' => '(site)/files/(:any)',
'options' => $files['file']
]
];

View file

@ -0,0 +1,55 @@
<?php
use Kirby\Toolkit\Escape;
return [
'pages' => [
'label' => t('pages'),
'icon' => 'page',
'query' => function (string $query = null) {
$pages = site()
->index(true)
->search($query)
->filter('isReadable', true)
->limit(10);
$results = [];
foreach ($pages as $page) {
$results[] = [
'image' => $page->panel()->image(),
'text' => Escape::html($page->title()->value()),
'link' => $page->panel()->url(true),
'info' => Escape::html($page->id())
];
}
return $results;
}
],
'files' => [
'label' => t('files'),
'icon' => 'image',
'query' => function (string $query = null) {
$files = site()
->index(true)
->filter('isReadable', true)
->files()
->search($query)
->limit(10);
$results = [];
foreach ($files as $file) {
$results[] = [
'image' => $file->panel()->image(),
'text' => Escape::html($file->filename()),
'link' => $file->panel()->url(true),
'info' => Escape::html($file->id())
];
}
return $results;
}
]
];

View file

@ -0,0 +1,26 @@
<?php
use Kirby\Cms\Find;
return [
'page' => [
'pattern' => 'pages/(:any)',
'action' => fn (string $path) => Find::page($path)->panel()->view()
],
'page.file' => [
'pattern' => 'pages/(:any)/files/(:any)',
'action' => function (string $id, string $filename) {
return Find::file('pages/' . $id, $filename)->panel()->view();
}
],
'site' => [
'pattern' => 'site',
'action' => fn () => site()->panel()->view()
],
'site.file' => [
'pattern' => 'site/files/(:any)',
'action' => function (string $filename) {
return Find::file('site', $filename)->panel()->view();
}
],
];

View file

@ -0,0 +1,11 @@
<?php
return function ($kirby) {
return [
'icon' => 'settings',
'label' => t('view.system'),
'menu' => true,
'dialogs' => require __DIR__ . '/system/dialogs.php',
'views' => require __DIR__ . '/system/views.php'
];
};

View file

@ -0,0 +1,43 @@
<?php
use Kirby\Panel\Field;
return [
// license registration
'registration' => [
'load' => function () {
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'license' => [
'label' => t('license.register.label'),
'type' => 'text',
'required' => true,
'counter' => false,
'placeholder' => 'K3-',
'help' => t('license.register.help')
],
'email' => Field::email([
'required' => true
])
],
'submitButton' => t('license.register'),
'value' => [
'license' => null,
'email' => null
]
]
];
},
'submit' => function () {
// @codeCoverageIgnoreStart
kirby()->system()->register(get('license'), get('email'));
return [
'event' => 'system.register',
'message' => t('license.register.success')
];
// @codeCoverageIgnoreEnd
}
],
];

View file

@ -0,0 +1,47 @@
<?php
use Kirby\Http\Server;
return [
'system' => [
'pattern' => 'system',
'action' => function () {
$kirby = kirby();
$system = $kirby->system();
$license = $system->license();
// @codeCoverageIgnoreStart
if ($license === true) {
// valid license, but user is not admin
$license = 'Kirby 3';
} elseif ($license === false) {
// no valid license
$license = null;
}
// @codeCoverageIgnoreEnd
$plugins = $system->plugins()->values(function ($plugin) {
return [
'author' => $plugin->authorsNames(),
'license' => $plugin->license(),
'link' => $plugin->link(),
'name' => $plugin->name(),
'version' => $plugin->version(),
];
});
return [
'component' => 'k-system-view',
'props' => [
'debug' => $kirby->option('debug', false),
'license' => $license,
'plugins' => $plugins,
'php' => phpversion(),
'server' => $system->serverSoftware(),
'https' => Server::https(),
'version' => $kirby->version(),
]
];
}
],
];

View file

@ -0,0 +1,14 @@
<?php
return function ($kirby) {
return [
'icon' => 'users',
'label' => t('view.users'),
'search' => 'users',
'menu' => true,
'dialogs' => require __DIR__ . '/users/dialogs.php',
'dropdowns' => require __DIR__ . '/users/dropdowns.php',
'searches' => require __DIR__ . '/users/searches.php',
'views' => require __DIR__ . '/users/views.php'
];
};

View file

@ -0,0 +1,295 @@
<?php
use Kirby\Cms\Find;
use Kirby\Cms\UserRules;
use Kirby\Exception\InvalidArgumentException;
use Kirby\Panel\Field;
use Kirby\Panel\Panel;
use Kirby\Toolkit\Escape;
$files = require __DIR__ . '/../files/dialogs.php';
return [
// create
'user.create' => [
'pattern' => 'users/create',
'load' => function () {
$kirby = kirby();
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'name' => Field::username(),
'email' => Field::email([
'link' => false,
'required' => true
]),
'password' => Field::password(),
'translation' => Field::translation([
'required' => true
]),
'role' => Field::role([
'required' => true
])
],
'submitButton' => t('create'),
'value' => [
'name' => '',
'email' => '',
'password' => '',
'translation' => $kirby->panelLanguage(),
'role' => $kirby->user()->role()->name()
]
]
];
},
'submit' => function () {
kirby()->users()->create([
'name' => get('name'),
'email' => get('email'),
'password' => get('password'),
'language' => get('translation'),
'role' => get('role')
]);
return [
'event' => 'user.create'
];
}
],
// change email
'user.changeEmail' => [
'pattern' => 'users/(:any)/changeEmail',
'load' => function (string $id) {
$user = Find::user($id);
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'email' => [
'label' => t('email'),
'required' => true,
'type' => 'email',
'preselect' => true
]
],
'submitButton' => t('change'),
'value' => [
'email' => $user->email()
]
]
];
},
'submit' => function (string $id) {
Find::user($id)->changeEmail(get('email'));
return [
'event' => 'user.changeEmail'
];
}
],
// change language
'user.changeLanguage' => [
'pattern' => 'users/(:any)/changeLanguage',
'load' => function (string $id) {
$user = Find::user($id);
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'translation' => Field::translation(['required' => true])
],
'submitButton' => t('change'),
'value' => [
'translation' => $user->language()
]
]
];
},
'submit' => function (string $id) {
Find::user($id)->changeLanguage(get('translation'));
return [
'event' => 'user.changeLanguage',
'reload' => [
'globals' => '$translation'
]
];
}
],
// change name
'user.changeName' => [
'pattern' => 'users/(:any)/changeName',
'load' => function (string $id) {
$user = Find::user($id);
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'name' => Field::username([
'preselect' => true
])
],
'submitButton' => t('rename'),
'value' => [
'name' => $user->name()->value()
]
]
];
},
'submit' => function (string $id) {
Find::user($id)->changeName(get('name'));
return [
'event' => 'user.changeName'
];
}
],
// change password
'user.changePassword' => [
'pattern' => 'users/(:any)/changePassword',
'load' => function (string $id) {
$user = Find::user($id);
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'password' => Field::password([
'label' => t('user.changePassword.new'),
]),
'passwordConfirmation' => Field::password([
'label' => t('user.changePassword.new.confirm'),
])
],
'submitButton' => t('change'),
]
];
},
'submit' => function (string $id) {
$user = Find::user($id);
$password = get('password');
$passwordConfirmation = get('passwordConfirmation');
// validate the password
UserRules::validPassword($user, $password ?? '');
// compare passwords
if ($password !== $passwordConfirmation) {
throw new InvalidArgumentException([
'key' => 'user.password.notSame'
]);
}
// change password if everything's fine
$user->changePassword($password);
return [
'event' => 'user.changePassword'
];
}
],
// change role
'user.changeRole' => [
'pattern' => 'users/(:any)/changeRole',
'load' => function (string $id) {
$user = Find::user($id);
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'role' => Field::role([
'label' => t('user.changeRole.select'),
'required' => true,
])
],
'submitButton' => t('user.changeRole'),
'value' => [
'role' => $user->role()->name()
]
]
];
},
'submit' => function (string $id) {
$user = Find::user($id)->changeRole(get('role'));
return [
'event' => 'user.changeRole',
'user' => $user->toArray()
];
}
],
// delete
'user.delete' => [
'pattern' => 'users/(:any)/delete',
'load' => function (string $id) {
$user = Find::user($id);
$i18nPrefix = $user->isLoggedIn() ? 'account' : 'user';
return [
'component' => 'k-remove-dialog',
'props' => [
'text' => tt($i18nPrefix . '.delete.confirm', [
'email' => Escape::html($user->email())
])
]
];
},
'submit' => function (string $id) {
$user = Find::user($id);
$redirect = false;
$referrer = Panel::referrer();
$url = $user->panel()->url(true);
$user->delete();
// redirect to the users view
// if the dialog has been opened in the user view
if ($referrer === $url) {
$redirect = '/users';
}
// logout the user if they deleted themselves
if ($user->isLoggedIn()) {
$redirect = '/logout';
}
return [
'event' => 'user.delete',
'dispatch' => ['content/remove' => [$url]],
'redirect' => $redirect
];
}
],
// change file name
'user.file.changeName' => [
'pattern' => '(users/.*?)/files/(:any)/changeName',
'load' => $files['changeName']['load'],
'submit' => $files['changeName']['submit'],
],
// change file sort
'user.file.changeSort' => [
'pattern' => '(users/.*?)/files/(:any)/changeSort',
'load' => $files['changeSort']['load'],
'submit' => $files['changeSort']['submit'],
],
// delete file
'user.file.delete' => [
'pattern' => '(users/.*?)/files/(:any)/delete',
'load' => $files['delete']['load'],
'submit' => $files['delete']['submit'],
]
];

View file

@ -0,0 +1,18 @@
<?php
use Kirby\Cms\Find;
$files = require __DIR__ . '/../files/dropdowns.php';
return [
'user' => [
'pattern' => 'users/(:any)',
'options' => function (string $id) {
return Find::user($id)->panel()->dropdown();
}
],
'user.file' => [
'pattern' => '(users/.*?)/files/(:any)',
'options' => $files['file']
]
];

View file

@ -0,0 +1,25 @@
<?php
use Kirby\Toolkit\Escape;
return [
'users' => [
'label' => t('users'),
'icon' => 'users',
'query' => function (string $query = null) {
$users = kirby()->users()->search($query)->limit(10);
$results = [];
foreach ($users as $user) {
$results[] = [
'image' => $user->panel()->image(),
'text' => Escape::html($user->username()),
'link' => $user->panel()->url(true),
'info' => Escape::html($user->role()->title())
];
}
return $results;
}
]
];

View file

@ -0,0 +1,65 @@
<?php
use Kirby\Cms\Find;
use Kirby\Toolkit\Escape;
return [
'users' => [
'pattern' => 'users',
'action' => function () {
$kirby = kirby();
$role = get('role');
$roles = $kirby->roles()->toArray(fn ($role) => [
'id' => $role->id(),
'title' => $role->title(),
]);
return [
'component' => 'k-users-view',
'props' => [
'role' => function () use ($kirby, $roles, $role) {
if ($role) {
return $roles[$role] ?? null;
}
},
'roles' => array_values($roles),
'users' => function () use ($kirby, $role) {
$users = $kirby->users();
if (empty($role) === false) {
$users = $users->role($role);
}
$users = $users->paginate([
'limit' => 20,
'page' => get('page')
]);
return [
'data' => $users->values(fn ($user) => [
'id' => $user->id(),
'image' => $user->panel()->image(),
'info' => Escape::html($user->role()->title()),
'link' => $user->panel()->url(true),
'text' => Escape::html($user->username())
]),
'pagination' => $users->pagination()->toArray()
];
},
]
];
}
],
'user' => [
'pattern' => 'users/(:any)',
'action' => function (string $id) {
return Find::user($id)->panel()->view();
}
],
'user.file' => [
'pattern' => 'users/(:any)/files/(:any)',
'action' => function (string $id, string $filename) {
return Find::file('users/' . $id, $filename)->panel()->view();
}
],
];

View file

@ -0,0 +1,2 @@
<?php /** @var \Kirby\Cms\Block $block */ ?>
<pre><code class="language-<?= $block->language()->or('text') ?>"><?= $block->code()->html(false) ?></code></pre>

View file

@ -0,0 +1,59 @@
name: field.blocks.code.name
icon: code
wysiwyg: true
preview: code
fields:
code:
label: field.blocks.code.name
type: textarea
placeholder: field.blocks.code.placeholder
buttons: false
font: monospace
language:
label: field.blocks.code.language
type: select
default: text
options:
bash: Bash
basic: BASIC
c: C
clojure: Clojure
cpp: C++
csharp: C#
css: CSS
diff: Diff
elixir: Elixir
elm: Elm
erlang: Erlang
go: Go
graphql: GraphQL
haskell: Haskell
html: HTML
java: Java
js: JavaScript
json: JSON
latext: LaTeX
less: Less
lisp: Lisp
lua: Lua
makefile: Makefile
markdown: Markdown
markup: Markup
objectivec: Objective-C
pascal: Pascal
perl: Perl
php: PHP
text: Plain Text
python: Python
r: R
ruby: Ruby
rust: Rust
sass: Sass
scss: SCSS
shell: Shell
sql: SQL
swift: Swift
typescript: TypeScript
vbnet: VB.net
xml: XML
yaml: YAML

View file

@ -0,0 +1,10 @@
<?php /** @var \Kirby\Cms\Block $block */ ?>
<figure>
<ul>
<?php foreach ($block->images()->toFiles() as $image): ?>
<li>
<?= $image ?>
</li>
<?php endforeach ?>
</ul>
</figure>

View file

@ -0,0 +1,15 @@
name: field.blocks.gallery.name
icon: dashboard
preview: gallery
fields:
images:
label: field.blocks.gallery.images.label
type: files
multiple: true
layout: cards
size: tiny
empty: field.blocks.gallery.images.empty
uploads:
template: blocks/image
image:
ratio: 1/1

View file

@ -0,0 +1,2 @@
<?php /** @var \Kirby\Cms\Block $block */ ?>
<<?= $level = $block->level()->or('h2') ?>><?= $block->text() ?></<?= $level ?>>

View file

@ -0,0 +1,24 @@
name: field.blocks.heading.name
icon: title
wysiwyg: true
preview: heading
fields:
level:
label: field.blocks.heading.level
type: select
empty: false
default: "h2"
width: 1/6
options:
- h1
- h2
- h3
- h4
- h5
- h6
text:
label: field.blocks.heading.text
type: writer
inline: true
width: 5/6
placeholder: field.blocks.heading.placeholder

View file

@ -0,0 +1,35 @@
<?php
/** @var \Kirby\Cms\Block $block */
$alt = $block->alt();
$caption = $block->caption();
$crop = $block->crop()->isTrue();
$link = $block->link();
$ratio = $block->ratio()->or('auto');
$src = null;
if ($block->location() == 'web') {
$src = $block->src()->esc();
} elseif ($image = $block->image()->toFile()) {
$alt = $alt ?? $image->alt();
$src = $image->url();
}
?>
<?php if ($src): ?>
<figure<?= attr(['data-ratio' => $ratio, 'data-crop' => $crop], ' ') ?>>
<?php if ($link->isNotEmpty()): ?>
<a href="<?= esc($link->toUrl()) ?>">
<img src="<?= $src ?>" alt="<?= $alt->esc() ?>">
</a>
<?php else: ?>
<img src="<?= $src ?>" alt="<?= $alt->esc() ?>">
<?php endif ?>
<?php if ($caption->isNotEmpty()): ?>
<figcaption>
<?= $caption ?>
</figcaption>
<?php endif ?>
</figure>
<?php endif ?>

View file

@ -0,0 +1,59 @@
name: field.blocks.image.name
icon: image
preview: image
fields:
location:
label: field.blocks.image.location
type: radio
columns: 2
default: "kirby"
options:
kirby: Kirby
web: Web
image:
label: field.blocks.image.name
type: files
multiple: false
image:
back: black
uploads:
template: blocks/image
when:
location: kirby
src:
label: field.blocks.image.url
type: url
when:
location: web
alt:
label: field.blocks.image.alt
type: text
icon: title
caption:
label: field.blocks.image.caption
type: writer
icon: text
inline: true
link:
label: field.blocks.image.link
type: text
icon: url
ratio:
label: field.blocks.image.ratio
type: select
placeholder: Auto
width: 1/2
options:
1/1: "1:1"
16/9: "16:9"
10/8: "10:8"
21/9: "21:9"
7/5: "7:5"
4/3: "4:3"
5/3: "5:3"
3/2: "3:2"
3/1: "3:1"
crop:
label: field.blocks.image.crop
type: toggle
width: 1/2

View file

@ -0,0 +1 @@
<hr />

View file

@ -0,0 +1,4 @@
name: field.blocks.line.name
icon: divider
preview: line
wysiwyg: true

View file

@ -0,0 +1,2 @@
<?php /** @var \Kirby\Cms\Block $block */ ?>
<?= $block->text();

View file

@ -0,0 +1,8 @@
name: field.blocks.list.name
icon: list-bullet
wysiwyg: true
preview: list
fields:
text:
label: field.blocks.list.name
type: list

View file

@ -0,0 +1,2 @@
<?php /** @var \Kirby\Cms\Block $block */ ?>
<?= $block->text()->kt();

View file

@ -0,0 +1,11 @@
name: field.blocks.markdown.name
icon: markdown
preview: markdown
wysiwyg: true
fields:
text:
label: field.blocks.markdown.label
placeholder: field.blocks.markdown.placeholder
type: textarea
buttons: false
font: monospace

View file

@ -0,0 +1,9 @@
<?php /** @var \Kirby\Cms\Block $block */ ?>
<blockquote>
<?= $block->text() ?>
<?php if ($block->citation()->isNotEmpty()): ?>
<footer>
<?= $block->citation() ?>
</footer>
<?php endif ?>
</blockquote>

View file

@ -0,0 +1,17 @@
name: field.blocks.quote.name
icon: quote
wysiwyg: true
preview: quote
fields:
text:
label: field.blocks.quote.text.label
placeholder: field.blocks.quote.text.placeholder
type: writer
inline: true
icon: quote
citation:
label: field.blocks.quote.citation.label
placeholder: field.blocks.quote.citation.placeholder
type: writer
inline: true
icon: user

View file

@ -0,0 +1,3 @@
name: Table
icon: menu
preview: table

View file

@ -0,0 +1,2 @@
<?php /** @var \Kirby\Cms\Block $block */ ?>
<?= $block->text();

View file

@ -0,0 +1,9 @@
name: field.blocks.text.name
icon: text
wysiwyg: true
preview: text
fields:
text:
type: writer
nodes: false
placeholder: field.blocks.text.placeholder

View file

@ -0,0 +1,9 @@
<?php /** @var \Kirby\Cms\Block $block */ ?>
<?php if ($video = video($block->url())): ?>
<figure>
<?= $video ?>
<?php if ($block->caption()->isNotEmpty()): ?>
<figcaption><?= $block->caption() ?></figcaption>
<?php endif ?>
</figure>
<?php endif ?>

View file

@ -0,0 +1,12 @@
name: field.blocks.video.name
icon: video
preview: video
fields:
url:
label: field.blocks.video.url.label
type: url
placeholder: field.blocks.video.url.placeholder
caption:
label: field.blocks.video.caption
type: writer
inline: true

View file

@ -0,0 +1,56 @@
name: Code
icon: code
fields:
code:
label: Code
type: textarea
buttons: false
font: monospace
language:
label: Language
type: select
default: text
options:
bash: Bash
basic: BASIC
c: C
clojure: Clojure
cpp: C++
csharp: C#
css: CSS
diff: Diff
elixir: Elixir
elm: Elm
erlang: Erlang
go: Go
graphql: GraphQL
haskell: Haskell
html: HTML
java: Java
js: JavaScript
json: JSON
latext: LaTeX
less: Less
lisp: Lisp
lua: Lua
makefile: Makefile
markdown: Markdown
markup: Markup
objectivec: Objective-C
pascal: Pascal
perl: Perl
php: PHP
text: Plain Text
python: Python
r: R
ruby: Ruby
rust: Rust
sass: Sass
scss: SCSS
shell: Shell
sql: SQL
swift: Swift
typescript: TypeScript
vbnet: VB.net
xml: XML
yaml: YAML

Some files were not shown because too many files have changed in this diff Show more